﻿using System;
using System.Collections.Generic;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Highlighting;
using System.IO;
using System.Xml;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Folding;
using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Search;
using System.Windows;
using System.Text;
using System.Windows.Data;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Document;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// 创建者：杨震宇
    /// 
    /// 说明：Markdown编辑器类。
    /// </summary>
    public class MarkdownEditorBase : TextEditor
    {
        /// <summary>
        /// 自定义高亮定义，完整定义。
        /// 但一千多行的帮助文档就明显延迟。
        /// </summary>
        private static IHighlightingDefinition advanceMarkdownHighlighting;

        /// <summary>
        /// 仅仅只提供基本的六级标题的高亮显示。
        /// </summary>
        private static IHighlightingDefinition onlyHeadersMarkdownHightlighting;

        /// <summary>
        /// [静态构造方法]载入高亮定义文件。
        /// </summary>
        static MarkdownEditorBase()
        {
            // 载入代码高亮的定义文件
            //using (Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.CustomHighlighting.xshd"))

            Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.Markdown.SyntaxHightlightRulesets.xshd");
            if (s != null)
            {
                using (XmlReader reader = new XmlTextReader(s))
                {
                    advanceMarkdownHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }

            Stream s2 = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.MarkdownHeader.SyntaxHightlightRulesets.xshd");
            if (s2 != null)
            {
                using (XmlReader reader = new XmlTextReader(s2))
                {
                    onlyHeadersMarkdownHightlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }

        /// <summary>
        /// 高亮显示方案。
        /// </summary>
        public enum HighlightingType
        {
            None,                              //禁用高亮
            OnlyHeaders,                       //只对六级标题高亮显示
            Advance,                           //全部显示
        }

        private MarkdownEditor masterEditor;
        /// <summary>
        /// [只读]父封装。
        /// </summary>
        public MarkdownEditor MasterEditor { get { return masterEditor; } }

        /// <summary>
        /// [构造方法]编辑器内核。
        /// </summary>
        public MarkdownEditorBase(MarkdownEditor masterEditor)
        {
            this.masterEditor = masterEditor;

            //这个不能写死，因为会导致“字体选择”功能无效化。
            //this.FontFamily = new FontFamily("SimSun");

            this.SnapsToDevicePixels = true;//避免折叠线模糊
            this.Background = Brushes.WhiteSmoke;

            this.WordWrap = true;
            this.ShowLineNumbers = true;
            this.Options.ShowColumnRuler = true;
            this.Options.ShowSpaces = false;
            //AvalonEdit本来使用间隔号代替空格，但间隔号是全角字符，要占据两个半角的宽度；
            //我把它改为使用加重号，这样就只需要占据一个半角宽度了。
            this.Options.ShowTabs = false;
            this.Options.AutoAlignTabs = false;//将Tab显示为“--+”，并禁止Tab自动伸缩尺寸。

            //this.Options.WordWrapIndentation = Globals.MainWindow.mainTabControl.FontSize * 2;
            //直接演示Markdown文档本身时，这个效果在同段换行时非常糟糕，
            //容易出现对不齐的现象——尤其使用的字体不是宋体时更易出问题。
            //TODO： 暂时去除它。

            TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);

            this.Options.ShowBoxForControlCharacters = true;
            this.Options.ShowEndOfLine = false;
            this.Options.AllowScrollBelowDocument = true;
            this.Options.HighlightCurrentLine = true;

            this.PreviewTextInput += MarkdownEditorBase_PreviewTextInput;
            this.TextChanged += MarkDownEditorBase_TextChanged;
            this.TextArea.SelectionChanged += TextArea_SelectionChanged;

            #region 代码高亮处理
            RefreshHighlightingSetting();
            #endregion

            this.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.DefaultIndentationStrategy();

            #region 代码折叠处理Folding
            foldingStrategy = new CustomFoldingStrategy(this);
            foldingManager = CustomFoldingManager.Install(this.TextArea);
            foldingStrategy.UpdateFoldings(FoldingManager, this.Document);

            foldingUpdateTimer.Interval = TimeSpan.FromSeconds(0.05);//50毫秒。
            foldingUpdateTimer.Tick += foldingUpdateTimer_Tick;
            foldingUpdateTimer.Start();
            #endregion

            #region 自动完成
            this.TextArea.TextEntered += TextArea_TextEntered;
            this.TextArea.TextEntering += TextArea_TextEntering;
            #endregion

            #region 搜索框
            if (searchPanel == null)
            {
                //searchPanel = new SearchPanel() { FontSize = Globals.MainWindow.FontSize, };
                //searchPanel.Attach(this.TextArea);
                searchPanel = SearchPanel.Install(this.TextArea);
            }
            #endregion

            //this.TextChanged += MarkdownEditorBase_TextChanged;

            //this.Document.UndoStack.MarkAsOriginalFile();
            //加上这句后，无法撤销已保存的文件。

            this.GotFocus += MarkDownEditorBase_GotFocus;
            this.LostFocus += MarkDownEditorBase_LostFocus;
            this.PreviewKeyDown += MarkDownEditorBase_PreviewKeyDown;

            //处理拖动
            this.AllowDrop = true;

            //处理字号缩放
            this.PreviewMouseWheel += MarkDownEditorBase_PreviewMouseWheel;

            this.PreviewMouseLeftButtonUp += MarkDownEditorBase_PreviewMouseLeftButtonUp;
            this.PreviewMouseDown += MarkDownEditorBase_PreviewMouseDown;

            //添加快捷工具条
            popupToolBar = new LunarMarkdownEditor.EditorPopupToolBar(this) { IsOpen = false, };
            this.MouseMove += MarkDownEditorBase_MouseMove;
        }

        private void MarkDownEditorBase_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.popupToolBar.IsOpen == false || this.popupToolBar.Entered == false) return;

            var topLeft = this.popupToolBar.VPanel.TranslatePoint(new Point(0, 0), this);
            var bottomRight = new Point(topLeft.X + this.popupToolBar.MainBorder.ActualWidth, topLeft.Y + this.popupToolBar.MainBorder.ActualHeight);
            //var topRight = new Point(bottomRight.X, topLeft.Y);
            //var bottomLeft = new Point(topLeft.X, bottomRight.Y);

            var rect1 = new Rect(topLeft, bottomRight);
            var rect2 = new Rect(new Point(topLeft.X - 25, topLeft.Y - 25), new Point(bottomRight.X + 25, bottomRight.Y + 25));
            var rect3 = new Rect(new Point(topLeft.X - 50, topLeft.Y - 50), new Point(bottomRight.X + 50, bottomRight.Y + 50));
            var rect4 = new Rect(new Point(topLeft.X - 75, topLeft.Y - 75), new Point(bottomRight.X + 75, bottomRight.Y + 75));

            var mpt = e.GetPosition(this);

            if (rect1.Contains(mpt))
            {
                this.popupToolBar.MainBorder.Opacity = 1;
            }
            else if (rect2.Contains(mpt))
            {
                this.popupToolBar.MainBorder.Opacity = 0.75;
            }
            else if (rect3.Contains(mpt))
            {
                this.popupToolBar.MainBorder.Opacity = 0.50;
            }
            else if (rect4.Contains(mpt))
            {
                this.popupToolBar.MainBorder.Opacity = 0.25;
            }
            else
            {
                this.popupToolBar.IsOpen = false;
                this.popupToolBar.MainBorder.Opacity = 1;
                this.popupToolBar.Entered = false;
            }
            //this.popupToolBar.TbInfo.Text = topLeft.ToString() + "|" + bottomRight.ToString() + "|" + mpt.ToString();
        }

        private void MarkDownEditorBase_LostFocus(object sender, RoutedEventArgs e)
        {
            this.PopupToolBar.IsOpen = false;
        }

        private void MarkDownEditorBase_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            this.PopupToolBar.IsOpen = false;
        }

        private void MarkDownEditorBase_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (masterEditor == null || masterEditor.IsCompareAreaEditor) return;

            if (PopupToolBar.IsOpen == true)
            {
                PopupToolBar.IsOpen = false;
            }

            if (this.SelectionLength <= 0 || Globals.MainWindow.IsPopupContextToolbarEnabled == false) return;

            PopupToolBar.IsOpen = true;
        }

        private EditorPopupToolBar popupToolBar;
        /// <summary>
        /// 上下文快捷工具栏。
        /// </summary>
        internal EditorPopupToolBar PopupToolBar
        {
            get
            {
                return popupToolBar;
            }
        }

        private void TextArea_SelectionChanged(object sender, EventArgs e)
        {
            if (Globals.MainWindow != null)
            {
                Globals.MainWindow.RefreshTextInfos();
            }
        }

        private void MarkDownEditorBase_TextChanged(object sender, EventArgs e)
        {
            if (Globals.MainWindow != null)
            {
                Globals.MainWindow.RefreshTextInfos();
            }
        }

        private void RefreshHighlightingSetting()
        {
            //this.SyntaxHighlighting.MainRuleSet.Rules.Clear();
            //上面这行会导致错误

            switch (highlightingSetting)
            {
                case HighlightingType.None:
                    {
                        this.SyntaxHighlighting = null;
                        break;
                    }
                case HighlightingType.OnlyHeaders:
                    {
                        HighlightingManager.Instance.RegisterHighlighting("Markdown Header Highlighting", new string[] { ".md" }, onlyHeadersMarkdownHightlighting);

                        this.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".md");
                        break;
                    }
                default:
                    {
                        HighlightingManager.Instance.RegisterHighlighting("Markdown Highlighting", new string[] { ".md" }, advanceMarkdownHighlighting);

                        this.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".md");
                        break;
                    }
            }

            //TODO: 载入用户自定义高亮方案。
            //this.SyntaxHighlighting.MainRuleSet.Rules.Add(new HighlightingRule() {  })
        }

        /// <summary>
        /// 按住 Ctrl 键 + 鼠标滚轮 缩放字号。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MarkDownEditorBase_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if ((Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) <= 0 && (Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) <= 0) return;

            if (e.Delta > 0)
            {
                Globals.MainWindow.FontSizeUp();
            }
            else if (e.Delta < 0)
            {
                Globals.MainWindow.FontSizeDown();
            }

            e.Handled = true;
        }

        /// <summary>
        /// 是否支持试题编辑。需要开启“IsAutoCompletionEnabled”。
        /// </summary>
        public bool IsExamEnabled { get; set; }

        private bool isAutoCompletionEnabled = false;
        /// <summary>
        /// 是否支持自动完成。
        /// </summary>
        public bool IsAutoCompletionEnabled
        {
            get
            {
                return isAutoCompletionEnabled;
            }
            set
            {
                isAutoCompletionEnabled = value;
                if (value == false)
                {
                    if (this.completionWindow != null &&
                        this.completionWindow.Visibility == Visibility.Visible)
                    {
                        this.completionWindow.Close();
                        this.completionWindow = null;
                    }
                }
            }
        }

        /// <summary>
        /// 是否显示空格。
        /// </summary>
        public bool IsShowSpaces
        {
            get { return this.Options.ShowSpaces; }
            set { this.Options.ShowSpaces = value; }
        }

        /// <summary>
        /// 是否显示段落标记。
        /// </summary>
        public bool IsShowEndOfLine
        {
            get { return this.Options.ShowEndOfLine; }
            set { this.Options.ShowEndOfLine = value; }
        }

        /// <summary>
        /// 是否显示Tab符。
        /// </summary>
        public bool IsShowTabs
        {
            get { return this.Options.ShowTabs; }
            set { this.Options.ShowTabs = value; }
        }

        /// <summary>
        /// 是否支持英译中词典的自动提示功能。需要开启“IsAutoCompletionEnabled”。
        /// </summary>
        public bool IsEnToChineseDictEnabled { get; set; } = false;

        private MarkdownEditorBase.HighlightingType highlightingSetting = MarkdownEditorBase.HighlightingType.Advance;
        /// <summary>
        /// 采用何种高亮显示方案。
        /// </summary>
        public MarkdownEditorBase.HighlightingType HighlightingSetting
        {
            get { return highlightingSetting; }
            set
            {
                highlightingSetting = value;
                RefreshHighlightingSetting();
            }
        }

        /// <summary>
        /// [覆盖方法]只有用这个办法才能使Alt+Up/Down生效。
        /// </summary>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyboardDevice.Modifiers == ModifierKeys.Alt
                && (e.KeyboardDevice.IsKeyDown(Key.RightAlt) || e.KeyboardDevice.IsKeyDown(Key.LeftAlt)))
            {
                if ((e.Key == Key.Up || e.SystemKey == Key.Up))
                {
                    SwapWithPreviewLine();
                    e.Handled = true;
                }
                else if ((e.Key == Key.Down || e.SystemKey == Key.Down))
                {
                    SwapWithNextLine();
                    e.Handled = true;
                }
            }
            base.OnKeyDown(e);
        }

        /// <summary>
        /// 处理常用快捷键。
        /// </summary>
        void MarkDownEditorBase_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            this.PopupToolBar.IsOpen = false;

            bool isCtrl = false ||
                (Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) > 0;

            bool isShift = false ||
                (Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0;

            bool isAlt = false ||
                (Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Down) > 0;

            try
            {
                switch (e.Key)
                {
                    case Key.OemSemicolon://分号
                        {
                            int startOffset = this.SelectionStart;
                            int endOffset = this.SelectionStart + this.SelectionLength;

                            var startLine = this.Document.GetLineByOffset(startOffset);
                            var endLine = this.Document.GetLineByOffset(endOffset);

                            if (isCtrl)
                            {
                                if (isShift)
                                {
                                    //取消这些行的备注（如果行首存在分号，取消之）
                                    for (int i = endLine.LineNumber; i >= startLine.LineNumber; i--)
                                    {
                                        var line = this.Document.GetLineByNumber(i);
                                        var text = this.Document.GetText(line.Offset, 1);
                                        if (text == ";" || text == "；")
                                        {
                                            this.Document.Remove(line.Offset, 1);
                                        }
                                    }
                                }
                                else
                                {
                                    //将这些行变成备注（在头部添加分号）
                                    for (int i = endLine.LineNumber; i >= startLine.LineNumber; i--)
                                    {
                                        var line = this.Document.GetLineByNumber(i);
                                        this.Document.Insert(line.Offset, "；");
                                    }
                                }

                                e.Handled = true;
                            }

                            break;
                        }
                    case Key.OemOpenBrackets://左花括号
                        {
                            if (isCtrl && !isAlt)
                            {
                                e.Handled = true;
                                SwitchTaskListItemOrDateTimeLineState(false, isShift);
                            }
                            break;
                        }
                    case Key.OemCloseBrackets://右花括号
                        {
                            if (isCtrl && !isAlt)
                            {
                                e.Handled = true;
                                SwitchTaskListItemOrDateTimeLineState(true, isShift);
                            }
                            break;
                        }
                    //另有用处：case Key.OemMinus:  //Markdown语法：前后各一个“*”包围的文本加粗。例如：这是一段*斜体*的文本。
                    case Key.I://为什么在这里处理？因为avalonEdit已经绑死Alt+Up，使用Alt+Up不起任何作用。
                        {
                            if (isCtrl)//移动到上一行的上方。
                            {
                                if (isShift)
                                {
                                    if (!isAlt) SwapWithPreviewLine();
                                }
                                else
                                {
                                    //斜体
                                    SwitchItalic();//Markdown语法：前后各一个“_”包围的文本加粗。例如：这是一段_斜体_的文本。
                                }
                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.K://为什么用这个？因为avalonEdit已经绑死Alt+Up，使用Alt+Up不起任何作用。
                        {
                            if (isCtrl && isShift && !isAlt)//移动到下一行的下方。
                            {
                                SwapWithNextLine();

                                e.Handled = true;
                            }
                            break;
                        }
                    //另有用处：case Key.Oem8: //Markdown语法：前后两组“**”包围的文本加粗。例如：这是一段**加粗**的文本。
                    case Key.B:    //Markdown语法：前后两组“__”包围的文本加粗。例如：这是一段__加粗__的文本。
                        {
                            if (isCtrl)
                            {
                                //加粗
                                SwitchBold();
                            }
                            break;
                        }
                    case Key.Back://BackSpace，退格键
                        {
                            if (isShift && !isCtrl && !isAlt)
                            {
                                //删除到行首
                                var textArea = this.TextArea;
                                if (textArea != null && textArea.IsKeyboardFocused)
                                {
                                    var curLine = this.Document.GetLineByOffset(this.SelectionStart);
                                    var curLineText = this.Document.GetText(curLine.Offset, curLine.Length);

                                    var fstSelectedLine = this.Document.GetLineByOffset(this.SelectionStart);

                                    this.Select(fstSelectedLine.Offset, textArea.Selection.Length + (this.SelectionStart - fstSelectedLine.Offset));
                                    textArea.PerformTextInput("");

                                    if (CustomMarkdownSupport.IsTreeListTextLine(curLineText))
                                    {
                                        FormatTextTable();
                                    }

                                    e.Handled = true;
                                }
                            }
                            break;
                        }
                    case Key.Enter://回车键
                        {
                            if (isCtrl)
                            {
                                if (!isShift)
                                {
                                    //在当前行下方插入一个新行，并移动光标。
                                    AppendANewLine();
                                    JumpToNextCell();
                                    e.Handled = true;
                                }
                            }
                            else
                            {
                                if (isShift)
                                {
                                    //在当前行上方插入一个新行，并移动光标。
                                    InsertANewLine();
                                    JumpToNextCell();
                                    e.Handled = true;
                                }
                                else
                                {
                                    //AvalonEdit有个默认行为：若一行文本以空格开头，无论在第一个非空格字符前按下回车，还是在这些空格前按下回车，
                                    //新行开头都不会保留空格！！！

                                    if (IsCompleting == false)
                                    {
                                        var textArea = this.TextArea;
                                        var doc = Document;

                                        #region 处理 Html Tag 的自动换行/缩进功能

                                        if (this.SelectionLength == 0)
                                        {
                                            var oldSelOffset = SelectionStart;
                                            var line = doc.GetLineByOffset(oldSelOffset);
                                            var lineText = doc.GetText(line.Offset, line.Length);
                                            var selPoint = oldSelOffset - line.Offset;
                                            var leftText = lineText.Substring(0, selPoint);
                                            var rightText = lineText.Substring(selPoint);
                                            string tagName;
                                            string preFixText;
                                            if (IsEndWithHtmlTag(leftText, out tagName, out preFixText) && rightText.ToLower().StartsWith($"</{tagName.ToLower()}>"))
                                            {
                                                var replaceText = "\r\n";
                                                var preFixBlanks = new string(' ', GetTextWidth(preFixText, false));
                                                replaceText += preFixBlanks;
                                                replaceText += "\r\n";               //为什么不能自动缩进？因为在 Markdown 中以四个空格开头的行会被当成代码块！
                                                replaceText += preFixBlanks;
                                                doc.Replace(oldSelOffset, 0, replaceText);
                                                Select(oldSelOffset + preFixBlanks.Length + 2, 0);
                                                e.Handled = true;
                                                break;
                                            }
                                            //break; //放在这里会导致树型文字表编辑功能失效。
                                        }
                                        #endregion

                                        #region 处理树型列表
                                        var curLine = CurrentDocumentLine();
                                        var curLineText = this.Document.GetText(curLine.Offset, curLine.Length);
                                        string header, tail; int level;
                                        if (curLine != null && CustomMarkdownSupport.IsImageLinkLine(curLineText) == false &&
                                            IsTreeListLine(curLineText, out header, out tail, out level))
                                        {
                                            e.Handled = true;
                                            InsertNewLineAndFormat(isShift);
                                            break;
                                        }
                                        #endregion

                                        if (textArea != null && textArea.IsKeyboardFocused)
                                        {
                                            var fstSelectedLine = this.Document.GetLineByOffset(this.SelectionStart);

                                            //回车换行时保留的前导字串：
                                            //^[\t 　☆○◎◇□△§※★●◆■▲>〉》]{ 1,}[ \t]*
                                            //处理用户自定义的前导字符。
                                            //顺序是：先A类，再B类，最后内置前导字符。基本原则：用户定义优先。

                                            var leaderTextRegs = Globals.MainWindow.LeaderTextRegsA;
                                            if (string.IsNullOrEmpty(leaderTextRegs) == false)
                                            {
                                                leaderTextRegs = ConvertToRegexTexts(leaderTextRegs);

                                                if (ReplaceLeaderText(leaderTextRegs, false))
                                                {
                                                    e.Handled = true; return;
                                                }
                                            }

                                            leaderTextRegs = Globals.MainWindow.LeaderTextRegsB;
                                            if (string.IsNullOrWhiteSpace(leaderTextRegs) == false)
                                            {
                                                leaderTextRegs = ConvertToRegexTexts(leaderTextRegs);

                                                if (ReplaceLeaderText(leaderTextRegs, true))
                                                {
                                                    e.Handled = true; return;
                                                }
                                            }

                                            leaderTextRegs = @"^[\^\t 　☆○◎◇□△§※★●◆■▲>〉》]{1,}[ 　\t]*";
                                            if (ReplaceLeaderText(leaderTextRegs, false))
                                            {
                                                e.Handled = true; return;
                                            }

                                            leaderTextRegs = @"^[" + Globals.MainWindow.ReplaceLeaderChar + "] {0,}";
                                            //默认情况下，以替换式自动添加的前导字串总是“$+空格串”样式的，这是为了便于连续输入“$+空格串”的新行。
                                            //但是用户可以指定另一字符。

                                            if (ReplaceLeaderText(leaderTextRegs, false))
                                            {
                                                e.Handled = true; return;
                                            }
                                        }
                                    }
                                }
                                //这个方法会造成无法自动滚动的问题。
                            }
                            break;
                        }
                    case Key.Tab://Tab键
                        {
                            if (!isCtrl && !isAlt)
                            {
                                if (IsCompleting == false)
                                {
                                    if (JumpToNextExamElement(isShift))
                                    {
                                        e.Handled = true;
                                        break;
                                    }
                                }

                                if (isShift)
                                {
                                    if (JumpOverMarks(ref e, isShift)) return;

                                    //2017年1月11日，调整到这里是为了让树型文字表中的括号对啥的也支持使用Tab键来越过去。
                                    #region 处理树型列表的层级调整
                                    if (MoveTreeListTableLevel(e, isShift)) return;
                                    #endregion

                                    #region 将光标移动到上一单元格
                                    var line = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
                                    if (line == null) break;
                                    var text = this.Document.GetText(line.Offset, line.Length);
                                    if (text.Contains("|") == false && text.Contains("｜") == false) break;

                                    if (this.SelectionStart == line.Offset && this.SelectionLength == 0)
                                    {
                                        if (this.SelectionStart - 2 >= 0)
                                        {
                                            this.Select(this.SelectionStart - 2, 0);//2是\r\n的长度。
                                        }
                                        e.Handled = true;
                                        break;
                                    }

                                    //先在text中查找前一个分割线
                                    int preIndex = -1;
                                    for (int i = this.SelectionStart - 1; i >= 0; i--)
                                    {
                                        char c = this.Text[i];
                                        if (c == '|' || c == '｜' || c == '\n' || c == '\r')
                                        {
                                            preIndex = i;
                                            break;
                                        }
                                    }

                                    //再找后一个分割线位置
                                    int nextIndex = -1;
                                    for (int i = this.SelectionStart; i < this.Text.Length; i++)
                                    {
                                        char c = this.Text[i];
                                        if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                                        {
                                            nextIndex = i;
                                            break;
                                        }
                                    }

                                    if (nextIndex >= 0)
                                    {
                                        if (preIndex >= 0 && nextIndex > preIndex)
                                        {
                                            if (Globals.MainWindow.SelectCellFirst &&
                                                (this.SelectionStart != preIndex + 1 || this.SelectionLength != nextIndex - preIndex - 1))
                                            {
                                                this.Select(preIndex + 1, nextIndex - preIndex - 1);
                                                e.Handled = true;
                                            }
                                            else
                                            {
                                                //如果已选中当前单元格所有文本，继续找上一个单元格并选定。
                                                var preIndex2 = -1;
                                                for (int i = preIndex - 1; i >= 0; i--)
                                                {
                                                    char c = this.Text[i];
                                                    if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                                                    {
                                                        preIndex2 = i;
                                                        break;
                                                    }
                                                }

                                                if (preIndex2 < 0 && preIndex > 0)
                                                {
                                                    this.Select(preIndex - 1, 0);
                                                    e.Handled = true;
                                                }
                                                if (preIndex2 < preIndex)
                                                {
                                                    this.Select(preIndex2 + 1, preIndex - preIndex2 - 1);
                                                    e.Handled = true;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            this.Select(0, 0);//整个文档开头
                                            e.Handled = true;
                                        }
                                    }
                                    else
                                    {
                                        //如果就在整个文档的最后一个位置，此时肯定找不到nextIndex
                                        //如果已选中当前单元格所有文本，继续找上一个单元格并选定。
                                        var preIndex2 = -1;
                                        for (int i = preIndex - 1; i >= 0; i--)
                                        {
                                            char c = this.Text[i];
                                            if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                                            {
                                                preIndex2 = i;
                                                break;
                                            }
                                        }

                                        if (preIndex2 < 0 && preIndex > 0)
                                        {
                                            this.Select(preIndex - 1, 0);
                                            e.Handled = true;
                                        }
                                        if (preIndex2 < preIndex)
                                        {
                                            this.Select(preIndex2 + 1, preIndex - preIndex2 - 1);
                                            e.Handled = true;
                                        }
                                    }
                                    #endregion
                                }
                                else
                                {
                                    if (IsCompleting == false)
                                    {
                                        if (JumpToNextCell())
                                        {
                                            e.Handled = true;
                                        }
                                        else
                                        {
                                            #region 输入后自动替换

                                            //TODO: 可以考虑实现这样的功能：
                                            //      输入如下文本：
                                            //          今天的日期是date
                                            //      然后按 Tab 键，自动变成：
                                            //          今天的日期是2018年3月15日
                                            if (AutoReplaceManager.Replace(Document, this.SelectionStart, this.SelectionLength))
                                            {
                                                e.Handled = true;
                                                return;
                                            }

                                            #endregion

                                            //跳过反引号，用在：按Ctrl+`输入一对反引号并将插入点置于两个反引号之间，输入一些文本后直接跳过右反引号继续输入其它文本。
                                            //这比按两个组合方向按键跳过去或者按方向键跳过去更方便。
                                            //*号对与_对情形类似。

                                            if (JumpOverMarks(ref e, isShift)) return;

                                            #region 旧版的跳出反引号、星号对的代码。
                                            //var index = this.SelectionStart + this.SelectionLength;
                                            //if (index < this.Document.TextLength)
                                            //{
                                            //    var nextChar = this.Document.Text[index];
                                            //    if (nextChar == '`')
                                            //    {
                                            //        this.Select(index + 1, 0);
                                            //        e.Handled = true;
                                            //    }
                                            //    else if (nextChar == '*')
                                            //    {
                                            //        if (index < this.Document.TextLength - 1)
                                            //        {
                                            //            var nextNextChar = this.Document.Text[index + 1];
                                            //            if (nextNextChar == '*')
                                            //            {
                                            //                this.Select(index + 2, 0);
                                            //                e.Handled = true;
                                            //            }
                                            //            else
                                            //            {
                                            //                this.Select(index + 1, 0);
                                            //                e.Handled = true;
                                            //            }
                                            //        }
                                            //        else
                                            //        {
                                            //            this.Select(index + 1, 0);
                                            //            e.Handled = true;
                                            //        }
                                            //    }
                                            //    else if (nextChar == '_')
                                            //    {
                                            //        if (index < this.Document.TextLength - 1)
                                            //        {
                                            //            var nextNextChar = this.Document.Text[index + 1];
                                            //            if (nextNextChar == '_')
                                            //            {
                                            //                this.Select(index + 2, 0);
                                            //                e.Handled = true;
                                            //            }
                                            //            else
                                            //            {
                                            //                this.Select(index + 1, 0);
                                            //                e.Handled = true;
                                            //            }
                                            //        }
                                            //        else
                                            //        {
                                            //            this.Select(index + 1, 0);
                                            //            e.Handled = true;
                                            //        }
                                            //    }
                                            //} 
                                            #endregion

                                            //2017年1月11日，调整到这里是为了让树型文字表中的括号对啥的也支持使用Tab键来越过去。
                                            #region 处理树型列表的层级调整
                                            if (MoveTreeListTableLevel(e, isShift)) return;
                                            #endregion
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    case Key.Z:
                        {
                            if (isAlt)
                            {
                                if (!isShift && isCtrl)
                                {
                                    this.FoldSelectedBlock();
                                }
                            }
                            else
                            {
                                if (!isShift && isCtrl)
                                {
                                    if (this.CanUndo) this.Undo();
                                    e.Handled = true;
                                }
                            }
                            break;
                        }
                    case Key.Y:
                        {
                            if (!isAlt && !isShift && isCtrl)
                            {
                                if (this.CanRedo) this.Redo();
                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.OemPeriod://句点键
                        {
                            if (isShift && isCtrl && !isAlt)
                            {
                                var fstSelLine = this.Document.GetLineByOffset(this.SelectionStart);
                                var lastSelLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);

                                this.BeginChange();

                                for (int lineNumber = fstSelLine.LineNumber; lineNumber <= lastSelLine.LineNumber; lineNumber++)
                                {
                                    var line = this.Document.GetLineByNumber(lineNumber);
                                    var lineText = this.Document.GetText(line);

                                    lineText = "> " + lineText;
                                    this.Document.Replace(line.Offset, line.Length, lineText);
                                }

                                this.EndChange();
                            }
                            break;
                        }
                    case Key.OemComma://逗号健
                        {
                            if (isShift && isCtrl && !isAlt)
                            {
                                var fstSelLine = this.Document.GetLineByOffset(this.SelectionStart);
                                var lastSelLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);

                                Regex reg = new Regex(@"^[ 　]{0,3}[>〉》][ 　]{0,}");

                                this.BeginChange();

                                for (int lineNumber = fstSelLine.LineNumber; lineNumber <= lastSelLine.LineNumber; lineNumber++)
                                {
                                    var line = this.Document.GetLineByNumber(lineNumber);
                                    var lineText = this.Document.GetText(line);
                                    var match = reg.Match(lineText);
                                    if (match.Success)
                                    {
                                        lineText = lineText.Substring(match.Length);
                                        this.Document.Replace(line.Offset, line.Length, lineText);
                                    }
                                }

                                this.EndChange();
                            }
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show("发生意外错误，无法执行操作。建议保存文件并重启程序。\r\n" +
                    ex.Message + "\r\n" + ex.StackTrace, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 注意，它与 MarkdownEditor 类下的 IsEndOfHtmlTag() 方法的计算方法是不同的。
        /// </summary>
        /// <param name="leftText"></param>
        /// <param name="tagName"></param>
        /// <param name="preFixText"></param>
        /// <returns></returns>
        private static bool IsEndWithHtmlTag(string leftText, out string tagName, out string preFixText)
        {
            if (string.IsNullOrWhiteSpace(leftText))
            {
                tagName = ""; preFixText = ""; return false;
            }

            //这里用正则太复杂，要考虑反向、Attribute 值带尖括号等特殊情况，
            //还是就简单点好了——反正平时我也用不到这些特殊情况。

            var preIndex = leftText.LastIndexOf('<');
            if (preIndex < 0) { tagName = ""; preFixText = ""; return false; }
            var nextCharIndex = preIndex + 1;
            if (nextCharIndex >= leftText.Length) { tagName = ""; preFixText = ""; return false; }

            var nextChar = leftText[nextCharIndex];
            if (nextChar == ' ' || nextChar == '　' || nextChar == '\t') { tagName = ""; preFixText = ""; return false; }

            var nextBlankIndex = leftText.IndexOf(' ', nextCharIndex);
            if (nextBlankIndex < 0)
            {
                tagName = leftText.Substring(preIndex + 1, leftText.Length - preIndex - 2);
                preFixText = leftText.Substring(0, preIndex);
                return true;
            }

            tagName = leftText.Substring(preIndex + 1, nextBlankIndex - preIndex - 1);
            preFixText = leftText.Substring(0, preIndex);
            return true;
        }

        /// <summary>
        /// 保存的正则表达式是多行的，每行一个规则，这个方法是用来将这些规则合并为一个规则字串的。
        /// 合并后的样式类似：(规则一)|(规则二)|(规则三)
        /// </summary>
        /// <param name="leaderTextRegs"></param>
        /// <returns></returns>
        private static string ConvertToRegexTexts(string leaderTextRegs)
        {
            var spans = leaderTextRegs.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

            if (spans.Length > 0)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("(");
                foreach (var span in spans)
                {
                    sb.Append(span);
                    sb.Append(")|(");
                }
                leaderTextRegs = sb.ToString();
                if (leaderTextRegs.EndsWith("|("))
                    leaderTextRegs = leaderTextRegs.Substring(0, leaderTextRegs.Length - 2);
            }

            return leaderTextRegs;
        }

        /// <summary>
        /// 按Enter时在新行头部替换添加“前导字符串”。
        /// </summary>
        /// <param name="regexText">当前行头部前导字符的正则表达式规则。</param>
        /// <param name="replaceMode">是否替换为“$+空格串”的样式。为false时原文输出。</param>
        /// <returns></returns>
        private bool ReplaceLeaderText(string regexText, bool replaceMode)
        {
            if (string.IsNullOrEmpty(regexText)) return false;

            var textArea = this.TextArea;
            var fstSelectedLine = this.Document.GetLineByOffset(this.SelectionStart);
            var lineText = textArea.Document.GetText(fstSelectedLine.Offset, fstSelectedLine.Length);

            try
            {
                var regex = new Regex(regexText);
                var match = regex.Match(lineText);
                if (match.Success == false) return false;

                if (match.Value == lineText)//如果只添加了标记，却没有其它文本内容，直接取消标记。
                {
                    textArea.TextEditor.BeginChange();
                    textArea.TextEditor.Select(fstSelectedLine.Offset, this.SelectionStart - fstSelectedLine.Offset + this.SelectionLength);
                    textArea.TextEditor.SelectedText = "\r\n";
                    textArea.TextEditor.Select(fstSelectedLine.Offset + 2, 0);
                    textArea.TextEditor.EndChange();
                    textArea.BringIntoView();
                }
                else
                {
                    if (replaceMode)
                    {
                        //将匹配的文本转换为半角字符组成的等字符宽度字串
                        //一个全角字符被视为两个半角空格的字符宽度
                        //这里的“字符宽度”只考虑全、半角，不考虑字体带来的宽度变化

                        string spaceText;
                        var length = GetTextWidth(match.Value);
                        if (length > 1)
                        {
                            spaceText = Globals.MainWindow.ReplaceLeaderChar + new string(' ', length - 1);
                        }
                        else { spaceText = Globals.MainWindow.ReplaceLeaderChar; }

                        textArea.PerformTextInput($"\r\n{spaceText}");
                    }
                    else
                    {
                        //当前行符合规则的前导字串原文添加到新行头部。
                        textArea.PerformTextInput($"\r\n{match.Value}");
                    }
                    textArea.BringIntoView();
                }
                return true;
            }
            catch (Exception ex)
            {
                LMessageBox.Show("要保留的前导字符串正则表达式有错误，请重写正则表达式。\r\n\r\n" + ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        /// <summary>
        /// 改变树型文字表行的层级。
        /// </summary>
        /// <param name="isShift">为真时（按Shift键+Tab键）层级减小一级（向左）；
        /// 为假时（只按Tab键）层级增加一级（向右）。</param>
        private bool MoveTreeListTableLevel(KeyEventArgs e, bool isShift)
        {
            var curLine = CurrentDocumentLine();
            string header, tail; int level;
            if (curLine != null && IsTreeListLine(this.Document.GetText(curLine.Offset, curLine.Length), out header, out tail, out level))
            {
                e.Handled = true;
                if (isShift) MoveTreeListLineLevelLeft(curLine, header, tail, level);
                else MoveTreeListLineLevelRight(curLine, header, tail, level);
                return true;
            }

            return false;
        }

        #region 处理树型列表


        /// <summary>
        /// 是否树型列表文本行。
        /// </summary>
        /// <param name="line">要判断的文本行。</param>
        /// <param name="header">用于返回此文本行头部的格式字符串。</param>
        /// <param name="tail">用于返回此文本行尾部的正文字符串。</param>
        /// <param name="level">用于返回此文本行的层级，从1开始。当此层级为1时，文本行形如：！XXX。</param>
        public static bool IsTreeListLine(string line, out string header, out string tail, out int level)
        {
            if (string.IsNullOrEmpty(line))
            {
                header = "";
                tail = "";
                level = 0;//0表示不是树型列表文本行。
                return false;
            }

            Regex rgx = new Regex(@"^[!！][│　]{0,}[├└]{0,}(?!([ 　\t]*\{))");
            var match = rgx.Match(line);
            if (match.Success)
            {
                header = match.Value;
                tail = line.Substring(match.Length);
                level = header.Length;
                return true;
            }
            else
            {
                header = tail = "";
                level = 0;//0表示不是树型列表文本行。
                return false;
            }
        }

        private DocumentLine CurrentDocumentLine()
        {
            return this.Document.GetLineByOffset(this.SelectionStart);
        }

        private string CurrentLine()
        {
            int preReturnIndex = this.Text.LastIndexOf("\r\n", this.SelectionStart);
            int nextReturnIndex = this.Text.IndexOf("\r\n", this.SelectionStart);

            if (preReturnIndex < 0) preReturnIndex = -2;//后面会加2（\r\n's Length）
            if (nextReturnIndex < 0) nextReturnIndex = this.Text.Length;

            var length = nextReturnIndex - preReturnIndex - 2;
            if (length < 0) length = 0;

            return this.Text.Substring(preReturnIndex + 2, length);
        }

        private DocumentLine PreviewDocumentLine()
        {
            var curLine = CurrentDocumentLine();
            if (curLine == null || curLine.Offset <= 0) return null;

            return this.Document.GetLineByOffset(curLine.Offset - 2);//-\r\n's Length
        }

        private string PreviewLine()
        {
            int preReturnIndex = this.Text.LastIndexOf("\r\n", this.SelectionStart);
            if (preReturnIndex <= 0) return "";

            int prepreReturnIndex = this.Text.LastIndexOf("\r\n", preReturnIndex - 1);
            if (prepreReturnIndex < 0) return this.Text.Substring(0, preReturnIndex);

            return this.Text.Substring(prepreReturnIndex + 2, preReturnIndex - prepreReturnIndex - 2);
        }

        private DocumentLine NextDocumentLine()
        {
            var curLine = CurrentDocumentLine();

            //当插入点在最后一个字符时，TextLength==lastLine.EndOffset
            if (curLine == null || curLine.EndOffset >= this.Document.TextLength) return null;

            return this.Document.GetLineByOffset(curLine.EndOffset + 2);//+\r\n's Length
        }

        private string NextLine()
        {
            int nextReturnIndex = this.Text.IndexOf("\r\n", this.SelectionStart);
            if (nextReturnIndex < 0) return "";

            int nextnextReturnIndex = this.Text.IndexOf("\r\n", nextReturnIndex + 2);

            if (nextnextReturnIndex < 0) return this.Text.Substring(nextReturnIndex + 2);

            return this.Text.Substring(nextReturnIndex + 2, nextnextReturnIndex - nextReturnIndex - 2);
        }

        /// <summary>
        /// 根据当前行的上一行决定可以移动的层级，并进行整体格式化。
        /// 左移可连续多次移动，不需要考虑上一行的层级，但需要考虑上一行的格式化。
        /// 左移前跟随的所有下级均须同步左移。
        /// </summary>
        private void MoveTreeListLineLevelLeft(DocumentLine curLine, string header, string tail, int level)
        {
            if (level <= 1) return;//1级标题不能再左移了。

            //左移时，所有级别高于当前行的、跟随在当前行之后的都顺序左移一级。

            //基本思路：本行左移时，先自动生成本行的基准头部。
            //          由于本行左移时，可能对前面所有行产生影响，所以要对前面的行重新计算格式字符；
            //          本行左移时，可能对后面所有行产生影响，所以还需要对后面的行重新计算格式字符。
            //          →所以不如寻找头、尾，然后依次重新写格式字符。
            //层级标准：只有一个！号的算1级，[！│　├]这四个字符（其中一个是全角空格）每有一个加1级。

            List<TreeListTextLine> previewLines = new List<TreeListTextLine>();

            var preLine = curLine.PreviousLine;
            while (preLine != null)
            {
                var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                string preHeader, preTail; int preLevel;
                if (IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel))
                {
                    previewLines.Add(new TreeListTextLine()
                    {
                        Line = preLine,
                        OldText = preLineText,
                        NewText = "",
                        //HeaderTextArray = preHeader.ToCharArray(),
                        TailText = preTail,
                        OldLevel = preLevel,
                        NewLevel = preLevel,   //之前的行层级不变
                    });

                    preLine = preLine.PreviousLine;
                }
                else break;
            }

            List<TreeListTextLine> allLines = new List<TreeListTextLine>();

            for (int i = previewLines.Count - 1; i >= 0; i--)
            {
                allLines.Add(previewLines[i]);
            }

            List<TreeListTextLine> moveLines = new List<TreeListTextLine>();

            moveLines.Add(new TreeListTextLine()
            {
                Line = curLine,
                //HeaderTextArray = header.ToCharArray(),
                NewText = "",
                OldText = this.Document.GetText(curLine.Offset, curLine.Length),
                TailText = tail,
                OldLevel = level,
                NewLevel = level - 1,      //当前行层级降一级
            });
            allLines.Add(moveLines[0]);

            var nextLine = curLine.NextLine;
            while (nextLine != null)
            {
                string oldNextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                string nextHeader, nextTail; int nextLevel;
                if (IsTreeListLine(oldNextLineText, out nextHeader, out nextTail, out nextLevel))
                {
                    if (nextLevel > level)
                    {
                        var nextTreeListTextLine = new TreeListTextLine()
                        {
                            Line = nextLine,
                            //HeaderTextArray = nextHeader.ToCharArray(),
                            NewText = "",
                            OldText = oldNextLineText,
                            TailText = nextTail,
                            OldLevel = nextLevel,
                            NewLevel = nextLevel - 1,             //层级高于当前行的也降一级
                        };
                        moveLines.Add(nextTreeListTextLine);
                        allLines.Add(nextTreeListTextLine);      //无论层级都应加入此列表
                    }
                    else break;
                }
                else break;

                nextLine = nextLine.NextLine;
            }

            while (nextLine != null)
            {
                string oldNextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                string nextHeader, nextTail; int nextLevel;
                if (IsTreeListLine(oldNextLineText, out nextHeader, out nextTail, out nextLevel))
                {
                    var nextTreeListTextLine = new TreeListTextLine()
                    {
                        Line = nextLine,
                        //HeaderTextArray = nextHeader.ToCharArray(),
                        NewText = "",
                        OldText = oldNextLineText,
                        TailText = nextTail,
                        OldLevel = nextLevel,
                        NewLevel = nextLevel,             //当碰到一个层级与当前行移位前层级相同的行后，之后所有行都不再降级。
                    };
                    allLines.Add(nextTreeListTextLine);
                }
                else break;
                nextLine = nextLine.NextLine;
            }

            //重新格式化。
            FormatAllTreeLines(allLines, true);
        }

        /// <summary>
        /// 根据当前行的上一行决定可以移动的层级，并进行整体格式化。
        /// 右移最多移动到上一行的下一级。
        /// 右移前跟随的所有下级同步右移一级。
        /// </summary>
        private void MoveTreeListLineLevelRight(DocumentLine curLine, string header, string tail, int level)
        {
            if (curLine == null) return;

            //右移时最多移动到上一行的下一级。
            //如果上一行不是树型列表项，禁止；如果上一行是树型列表项，最多移动到上一行的更高1级。

            var preLine = curLine.PreviousLine;
            if (preLine == null) return;

            var pText = this.Document.GetText(preLine.Offset, preLine.Length);
            string pHeader, pTail; int pLevel;
            if (IsTreeListLine(pText, out pHeader, out pTail, out pLevel))
            {
                if (level > pLevel) return;
            }
            else return;

            List<TreeListTextLine> allLines = new List<TreeListTextLine>();

            List<TreeListTextLine> previewLines = new List<TreeListTextLine>();

            while (preLine != null)
            {
                var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                string preHeader, preTail; int preLevel;
                if (IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel))
                {
                    previewLines.Add(new TreeListTextLine()
                    {
                        Line = preLine,
                        OldText = preLineText,
                        NewText = "",
                        //HeaderTextArray = preHeader.ToCharArray(),
                        TailText = preTail,
                        OldLevel = preLevel,
                        NewLevel = preLevel,   //之前的行层级不变
                    });

                    preLine = preLine.PreviousLine;
                }
                else break;
            }

            for (int i = previewLines.Count - 1; i >= 0; i--)
            {
                allLines.Add(previewLines[i]);
            }

            List<TreeListTextLine> moveLines = new List<TreeListTextLine>();

            moveLines.Add(new TreeListTextLine()
            {
                Line = curLine,
                //HeaderTextArray = header.ToCharArray(),
                NewText = "",
                OldText = this.Document.GetText(curLine.Offset, curLine.Length),
                TailText = tail,
                OldLevel = level,
                NewLevel = level + 1,      //当前行层级降一级
            });
            allLines.Add(moveLines[0]);

            var nextLine = curLine.NextLine;
            while (nextLine != null)
            {
                string oldNextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                string nextHeader, nextTail; int nextLevel;
                if (IsTreeListLine(oldNextLineText, out nextHeader, out nextTail, out nextLevel))
                {
                    if (nextLevel > level)
                    {
                        var nextTreeListTextLine = new TreeListTextLine()
                        {
                            Line = nextLine,
                            //HeaderTextArray = nextHeader.ToCharArray(),
                            NewText = "",
                            OldText = oldNextLineText,
                            TailText = nextTail,
                            OldLevel = nextLevel,
                            NewLevel = nextLevel + 1,             //层级高于当前行的也升一级
                        };
                        moveLines.Add(nextTreeListTextLine);
                        allLines.Add(nextTreeListTextLine);      //无论层级都应加入此列表
                    }
                    else break;
                }
                else break;

                nextLine = nextLine.NextLine;
            }

            while (nextLine != null)
            {
                string oldNextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                string nextHeader, nextTail; int nextLevel;
                if (IsTreeListLine(oldNextLineText, out nextHeader, out nextTail, out nextLevel))
                {
                    var nextTreeListTextLine = new TreeListTextLine()
                    {
                        Line = nextLine,
                        //HeaderTextArray = nextHeader.ToCharArray(),
                        NewText = "",
                        OldText = oldNextLineText,
                        TailText = nextTail,
                        OldLevel = nextLevel,
                        NewLevel = nextLevel,             //当碰到一个层级与当前行移位前层级相同的行后，之后所有行都不再升级。
                    };
                    allLines.Add(nextTreeListTextLine);
                }
                else break;
                nextLine = nextLine.NextLine;
            }

            //重新格式化。
            FormatAllTreeLines(allLines);
        }

        private void FormatAllTreeLines(List<TreeListTextLine> allLines, bool? selectLeft = false)
        {
            if (allLines == null || allLines.Count <= 0) return;

            foreach (var tLine in allLines)
            {
                tLine.HeaderTextArray = BuildTreeLineBaseHeader(tLine.NewLevel).ToCharArray();
            }

            int basePreLevel = 0;

            for (int i = 0; i < allLines.Count; i++)
            {
                var tLine = allLines[i];
                if (tLine.OldLevel > basePreLevel + 1)
                {
                    tLine.NewLevel = basePreLevel + 1;
                }
                basePreLevel = tLine.NewLevel;
            }

            for (int i = 0; i < allLines.Count; i++)
            {
                var tLine = allLines[i];

                //格式化冒号开头的注释
                if (tLine.TailText.StartsWith(":"))
                {
                    tLine.TailText = "：" + tLine.TailText.Substring(1);
                }

                var newHeaderText = BuildTreeLineBaseHeader(tLine.NewLevel);
                tLine.NewText = newHeaderText + tLine.TailText;
                tLine.HeaderTextArray = newHeaderText.ToCharArray();

                //根据情况刷新竖线。
                if (tLine.NewLevel <= 1) continue;

                for (int j = i - 1; j >= 0; j--)
                {
                    var aPreLine = allLines[j];
                    if (aPreLine.NewLevel < tLine.NewLevel) break;
                    if (aPreLine.NewLevel == tLine.NewLevel)
                    {
                        aPreLine.HeaderTextArray[tLine.NewLevel - 1] = '├';
                        break;
                    }

                    if (aPreLine.NewLevel > tLine.NewLevel)
                    {
                        aPreLine.HeaderTextArray[tLine.NewLevel - 1] = '│';
                    }
                }
            }

            StringBuilder sb = new StringBuilder();
            foreach (var line in allLines)
            {
                sb.Append(new string(line.HeaderTextArray) + line.TailText);
                sb.Append("\r\n");
            }

            this.BeginChange();

            var oldSelectionStart = this.SelectionStart;
            var oldSelectionLength = this.SelectionLength;

            var newText = sb.ToString();
            if (newText.EndsWith("\r\n"))
            {
                newText = newText.Substring(0, newText.Length - 2);
            }

            this.Document.Replace(allLines[0].Line.Offset, allLines[allLines.Count - 1].Line.EndOffset - allLines[0].Line.Offset, newText);

            if (selectLeft.HasValue)
            {
                if (selectLeft.Value)
                {
                    if (oldSelectionStart > 0)
                    {
                        this.Select(oldSelectionStart - 1, oldSelectionLength);
                    }
                    else { this.Select(0, 0); }
                }
                else
                {
                    if (oldSelectionLength < this.Document.TextLength - 1)
                    {
                        this.Select(oldSelectionStart + 1, oldSelectionLength);
                    }
                }
            }

            this.EndChange();
        }

        private string BuildTreeLineBaseHeader(int level)
        {
            if (level <= 0) return "";
            if (level == 1) return "！";
            if (level == 2) return "！└";

            return $"！{new string('　', level - 2)}└";
        }

        private void InsertNewLineAndFormat(bool insertAtPreview = false)
        {
            var curLine = CurrentDocumentLine();
            var preLine = PreviewDocumentLine();
            var nextLine = NextDocumentLine();

            var curLineText = curLine == null ? "" : this.Document.GetText(curLine.Offset, curLine.Length);
            var preLineText = preLine == null ? "" : this.Document.GetText(preLine.Offset, preLine.Length);
            var nextLineText = nextLine == null ? "" : this.Document.GetText(nextLine.Offset, nextLine.Length);

            //MessageBox.Show($"P行：{preLineText}\r\nC行：{curLineText}\r\nN行：{nextLineText}");

            string headerText, tailText;
            int level;
            if (IsTreeListLine(curLineText, out headerText, out tailText, out level))
            {
                if (this.SelectionStart == curLine.Offset && this.SelectionLength == 0)
                {
                    this.BeginChange();
                    this.Document.Insert(curLine.Offset, "\r\n");
                    this.EndChange();

                    this.Select(curLine.Offset + 2, 0);
                    return;
                }

                this.BeginChange();

                int startReplaceOffset = this.SelectionStart;
                int tailStartOffset = curLine.Offset + headerText.Length;
                int replaceLength = curLine.EndOffset - this.SelectionStart;
                if (startReplaceOffset < tailStartOffset)
                {
                    startReplaceOffset = tailStartOffset;
                    replaceLength = curLine.EndOffset - startReplaceOffset;
                }
                var curTail = this.Document.GetText(startReplaceOffset, replaceLength);
                this.Select(this.SelectionStart, 0);

                var mark = "└";
                string nextHeaderText, nextTailText; int nextLevel;
                if (IsTreeListLine(nextLineText, out nextHeaderText, out nextTailText, out nextLevel) && nextLevel == level)
                {
                    mark = "├";
                }

                if (NextBrotherLineExist(curLine, level))
                {
                    mark = "├";
                }
                else
                {
                    mark = "└";
                }

                if (level == 1) mark = "！";

                if (curLine.TotalLength == curLine.Length)//没有定界符（\r\n）最后一行
                {
                    this.Document.Insert(curLine.EndOffset, "\r\n" + headerText.Substring(0, headerText.Length - 1) + $"{mark}{curTail}\r\n");
                }
                else
                {
                    this.Document.Insert(curLine.EndOffset + 2, headerText.Substring(0, headerText.Length - 1) + $"{mark}{curTail}\r\n");
                }

                this.Select(curLine.EndOffset + 2 + headerText.Length, 0);

                if (replaceLength > 0)//这个替换必须放到后面进行
                {
                    this.Document.Replace(startReplaceOffset, replaceLength, "");
                }

                FormatPreviewBrotherLines(this.Document.GetLineByNumber(curLine.LineNumber + 1), level);
                this.EndChange();
            }
        }

        private bool NextBrotherLineExist(DocumentLine curLine, int level)
        {
            if (level <= 0) return false;
            if (curLine == null || curLine.LineNumber >= this.Document.LineCount) return false;

            var nextLine = this.Document.GetLineByNumber(curLine.LineNumber + 1);
            while (nextLine != null)
            {
                string nextHeader, nextTail; int nextLevel;
                if (IsTreeListLine(this.Document.GetText(nextLine.Offset, nextLine.Length), out nextHeader, out nextTail, out nextLevel))
                {
                    if (nextLevel < level) return false;//截断
                    if (nextLevel == level) return true;
                    else
                    {
                        if (nextLine.LineNumber == this.Document.LineCount) return false;
                        nextLine = this.Document.GetLineByNumber(nextLine.LineNumber + 1);
                        continue;
                    }
                }
                else
                {
                    if (nextLine.LineNumber == this.Document.LineCount) return false;
                    nextLine = this.Document.GetLineByNumber(nextLine.LineNumber + 1);
                    continue;
                }
            }

            return false;
        }

        private void FormatPreviewBrotherLines(DocumentLine curLine, int level)
        {
            if (level == 1) return;//level==1时，只有惊叹号开头，其它不论。

            if (curLine == null || curLine.LineNumber <= 1) return;
            DocumentLine preLine = null;
            preLine = this.Document.GetLineByNumber(curLine.LineNumber - 1);

            while (preLine != null)
            {
                var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                string headerText, tailText; int preLevel;
                if (IsTreeListLine(preLineText, out headerText, out tailText, out preLevel) == false) return;
                if (preLevel != level) break;

                if (preLineText[level - 1] != '├')
                {
                    this.Document.Replace(preLine.Offset + preLevel - 1, 1, "├");
                }

                if (preLine.LineNumber <= 1) break;
                preLine = this.Document.GetLineByNumber(preLine.LineNumber - 1);
            }
        }

        #endregion

        /// <summary>
        /// 跳转到下一个输入区域。这些输入区域包括：成对的小括弧之间，成对的大括弧之间，成对的中括弧之间等等。
        /// </summary>
        /// <param name="e"></param>
        private bool JumpOverMarks(ref KeyEventArgs e, bool isShift)
        {
            if (SelectionLength != 0) return false;
            //只有当插入点正好在某些特殊符号（及符号组合）前面时才起作用，用来跳过这些个特殊符号组合。

            try
            {
                var regexText = "(</?[a-zA-Z]{1,}( [^><]*)?>)|(\\*\\*)|(\\[\\=)|(\\=\\])|(__)|([_\\*“”‘’'\"`<>《》〈〉\\(\\)\\[\\]\\{\\}（）〔〕〈〉「」『』〖〗【】［］｛｝＂＇°])";

                Regex regex = GetRegEx(regexText, isShift);
                int start = regex.Options.HasFlag(RegexOptions.RightToLeft) ? SelectionStart : SelectionStart + SelectionLength;
                Match match = regex.Match(Text, start);

                if (!match.Success)  // start again from beginning or end
                {
                    if (regex.Options.HasFlag(RegexOptions.RightToLeft))
                        match = regex.Match(Text, Text.Length);
                    else
                        match = regex.Match(Text, 0);
                }

                if (match.Success)
                {
                    if (isShift)
                    {
                        if (match.Index + match.Length != SelectionStart) return false;

                        var destSel = match.Index;
                        if (destSel >= 0)
                        {
                            Select(destSel, 0);
                            TextLocation loc = Document.GetLocation(match.Index);
                            ScrollTo(loc.Line, loc.Column);

                            e.Handled = true;
                            return true;
                        }
                    }
                    else
                    {
                        if (match.Index != SelectionStart + SelectionLength) return false;

                        var destSel = match.Index + match.Length;
                        if (destSel <= Document.TextLength)
                        {
                            Select(destSel, 0);
                            TextLocation loc = Document.GetLocation(match.Index);
                            ScrollTo(loc.Line, loc.Column);

                            e.Handled = true;
                            return true;
                        }
                    }

                    return false;
                }

                return false;
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        /// <summary>
        /// 自动完成窗口目前是否可见，是否正在进行提示。
        /// </summary>
        private bool IsCompleting
        {
            get
            {
                if (this.CompletionWindow != null && this.CompletionWindow.Visibility == Visibility.Visible)
                    return true;
                else return false;

            }
        }

        /// <summary>
        /// 切换目标文本的任务列表状态标记。
        /// </summary>
        /// <param name="backOrder">决定正序还是逆序。</param>
        /// <param name="isShift">决定是作用于本行还是作用于本行所在的任务列表项。false时仅作用于本行文本。</param>
        public void SwitchTaskListItemOrDateTimeLineState(bool backOrder, bool isShift)
        {
            int startOffset = this.SelectionStart;
            int selectionLength = this.SelectionLength;
            int endOffset = this.SelectionStart + this.SelectionLength;

            var startLine = this.Document.GetLineByOffset(startOffset);
            var endLine = this.Document.GetLineByOffset(endOffset);

            var startLineText = this.Document.GetText(startLine.Offset, startLine.Length);

            if (startLineText.StartsWith("    ") || startLineText.StartsWith("\t"))
            {
                LMessageBox.Show("插入点所在的这行文本属于一个代码块，不能执行此命令！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (isShift)
            {
                if (CustomMarkdownSupport.IsTaskLine(startLineText) == false && startLine.LineNumber > 1)
                {
                    //如果当前行不是任务列表，则向文件头部倒找。
                    DocumentLine destLine = null;
                    destLine = this.Document.GetLineByNumber(startLine.LineNumber - 1);
                    while (destLine != null)
                    {
                        var destLineText = this.Document.GetText(destLine.Offset, destLine.Length);
                        if (CustomMarkdownSupport.IsTaskLine(destLineText))
                        {
                            break;
                        }
                        destLine = this.Document.GetLineByNumber(destLine.LineNumber - 1);
                    }

                    if (destLine != null)
                    {
                        this.Select(destLine.Offset, destLine.Length);
                        SwitchTaskListItemOrDateTimeLineState(backOrder, false);
                        this.Select(startLine.Offset, selectionLength);
                        return;
                    }
                }//如果本行就是任务列表，则直接改动本行即可，继续。
            }

            CustomMarkdownSupport.TaskListItemState state = GetTaskListItemState(startLineText);

            //只改第一行，避免无谓的错误，也可降低开发难度。
            //CustomMarkdownSupport.TaskListItemState destState;
            string newHeader;
            string oldHeader;
            if (backOrder)
            {
                switch (state)
                {
                    case CustomMarkdownSupport.TaskListItemState.Finished:
                        {
                            oldHeader = "[+]";
                            newHeader = "[%] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.Precessing:
                        {
                            oldHeader = "[%]";
                            //destState = CustomMarkdownSupport.TaskListItemState.Finished;
                            newHeader = "[-] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.UnStart:
                        {
                            oldHeader = "[-]";
                            //destState = CustomMarkdownSupport.TaskListItemState.Precessing;
                            newHeader = "[#] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.Aborted:
                        {
                            oldHeader = "[#]";
                            //destState = CustomMarkdownSupport.NotTaskListItem
                            newHeader = "[+] ";
                            break;
                        }
                    default:
                        {
                            oldHeader = "";
                            //destState = CustomMarkdownSupport.TaskListItemState.UnStart;
                            newHeader = "[#] ";
                            break;
                        }
                }
            }
            else
            {
                switch (state)
                {
                    case CustomMarkdownSupport.TaskListItemState.Finished:
                        {
                            oldHeader = "[+]";
                            //destState = CustomMarkdownSupport.TodoListItemState.Abort;
                            newHeader = "[#] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.Precessing:
                        {
                            oldHeader = "[%]";
                            //destState = CustomMarkdownSupport.TodoListItemState.Finished;
                            newHeader = "[+] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.UnStart:
                        {
                            oldHeader = "[-]";
                            //destState = CustomMarkdownSupport.TodoListItemState.Precessing;
                            newHeader = "[%] ";
                            break;
                        }
                    case CustomMarkdownSupport.TaskListItemState.Aborted:
                        {
                            oldHeader = "[#]";
                            //destState = CustomMarkdownSupport.NotTodoListItem
                            newHeader = "[-] ";
                            break;
                        }
                    default:
                        {
                            oldHeader = "";
                            //destState = CustomMarkdownSupport.TodoListItemState.UnStart;
                            newHeader = "[-] ";
                            break;
                        }
                }
            }

            if (CustomMarkdownSupport.IsDateLine(startLineText))
            {
                if (oldHeader == "")
                {
                    var rightIndex = startLineText.IndexOf("]");
                    if (rightIndex >= 0)
                    {
                        startLineText = startLineText.Substring(0, rightIndex + 1) + newHeader + " " + startLineText.Substring(rightIndex + 1).TrimStart(new char[] { ' ' });
                        this.Select(startLine.Offset, startLine.Length);
                        this.SelectedText = startLineText;
                        var indexTmp = startLineText.IndexOf("]");
                        if (indexTmp >= 0)
                        {
                            this.Select(startLine.Offset + indexTmp + newHeader.Length + 1, 0);
                        }
                        else
                        {
                            this.Select(startLine.Offset, 0);
                        }
                        return;
                    }
                }
                else
                {
                    var indexOfOldHeader = startLineText.IndexOf(oldHeader);
                    if (indexOfOldHeader >= 0)
                    {
                        startLineText = startLineText.Substring(0, indexOfOldHeader) + newHeader + startLineText.Substring(indexOfOldHeader + oldHeader.Length).TrimStart(new char[] { ' ' });

                        this.Select(startLine.Offset, startLine.Length);
                        this.SelectedText = startLineText;
                        var indexTmp = startLineText.IndexOf("]");
                        if (indexTmp >= 0)
                        {
                            this.Select(startLine.Offset + indexTmp + newHeader.Length + 1, 0);
                        }
                        else
                        {
                            this.Select(startLine.Offset, 0);
                        }
                        return;
                    }
                }
            }

            int index = startLineText.Length;
            for (int i = 0; i < startLineText.Length - 1; i++)
            {
                char c = startLineText[i];
                if (c != ' ' && c != '　' && c != '\t' && c != '[' && c != ']' && c != '+' && c != '-' && c != '%' && c != '#')
                {
                    index = i;
                    break;
                }
            }
            var tail = startLineText.Substring(index);
            this.Select(startLine.Offset, startLine.Length);
            this.SelectedText = newHeader + tail;
            this.Select(startLine.Offset + newHeader.Length, 0);
        }

        private CustomMarkdownSupport.TaskListItemState GetTaskListItemState(string lineText)
        {
            if (string.IsNullOrWhiteSpace(lineText)) return CustomMarkdownSupport.TaskListItemState.NotTaskListItem;

            var tmp = lineText.Replace(" ", "").Replace("\t", "").Replace("　", "");
            if (tmp.StartsWith("[-]")) return CustomMarkdownSupport.TaskListItemState.UnStart;
            if (tmp.StartsWith("[%]")) return CustomMarkdownSupport.TaskListItemState.Precessing;
            if (tmp.StartsWith("[+]")) return CustomMarkdownSupport.TaskListItemState.Finished;
            if (tmp.StartsWith("[#]")) return CustomMarkdownSupport.TaskListItemState.Aborted;

            if (CustomMarkdownSupport.IsDateLine(lineText))
            {
                if (tmp.IndexOf("[-]") >= 0) return CustomMarkdownSupport.TaskListItemState.UnStart;
                if (tmp.IndexOf("[%]") >= 0) return CustomMarkdownSupport.TaskListItemState.Precessing;
                if (tmp.IndexOf("[+]") >= 0) return CustomMarkdownSupport.TaskListItemState.Finished;
                if (tmp.IndexOf("[#]") >= 0) return CustomMarkdownSupport.TaskListItemState.Aborted;
            }

            return CustomMarkdownSupport.TaskListItemState.NotTaskListItem;
        }

        /// <summary>
        /// 在当前选定文本两侧加上倾斜标记文本。
        /// </summary>
        public void SwitchItalic()
        {
            if (this.SelectionLength == 0)
            {
                //this.Document.Insert(this.SelectionStart, "_");
                this.SelectedText = "__";
                this.Select(this.SelectionStart + 1, 0);
            }
            else
            {
                var lineEnd = this.Document.GetLineByOffset(this.SelectionStart).EndOffset;
                if (this.SelectionStart + this.SelectionLength > lineEnd) return;//不支持跨行

                if ((this.SelectedText.StartsWith("_") && this.SelectedText.EndsWith("_")) ||
                    (this.SelectedText.StartsWith("*") && this.SelectedText.EndsWith("*")))
                {
                    this.SelectedText = this.SelectedText.Substring(1, this.SelectedText.Length - 2);
                }
                else
                {
                    this.SelectedText = "*" + this.SelectedText + "*";
                }
            }
        }

        /// <summary>
        /// 在当前选定文本两侧加上或取消代码片段标记文本（``，即一对反引号）。
        /// </summary>
        public void SwitchCodeSnippet()
        {
            if (this.SelectionLength == 0)
            {
                this.SelectedText = "``";
                this.Select(this.SelectionStart + 1, 0);
            }
            else
            {
                var lineEnd = this.Document.GetLineByOffset(this.SelectionStart).EndOffset;
                if (this.SelectionStart + this.SelectionLength > lineEnd) return;//不支持跨行

                if (this.SelectedText.StartsWith("`") && this.SelectedText.EndsWith("`"))
                {
                    this.SelectedText = this.SelectedText.Substring(1, this.SelectedText.Length - 2);
                }
                else
                {
                    this.SelectedText = "`" + this.SelectedText + "`";
                }
            }
        }

        /// <summary>
        /// 在当前选定文本两侧加上加粗标记文本。
        /// </summary>
        public void SwitchBold()
        {
            //原本使用****语法，但易造成冲突，在引用块中导致高亮显示不正常
            if (this.SelectionLength == 0)
            {
                //this.Document.Insert(this.SelectionStart, "_");
                this.SelectedText = "____";
                this.Select(this.SelectionStart + 2, 0);
            }
            else
            {
                var lineEnd = this.Document.GetLineByOffset(this.SelectionStart).EndOffset;
                if (this.SelectionStart + this.SelectionLength > lineEnd) return;//不支持跨行


                if ((this.SelectedText.StartsWith("__") && this.SelectedText.EndsWith("__")) ||
                    (this.SelectedText.StartsWith("**") && this.SelectedText.EndsWith("**")))
                {
                    this.SelectedText = this.SelectedText.Substring(2, this.SelectedText.Length - 4);
                }
                else
                {
                    this.SelectedText = "__" + this.SelectedText + "__";
                }
            }
        }

        /// <summary>
        /// 折叠插入点所在的可折叠区域。
        /// </summary>
        public void FoldSelectedBlock()
        {
            ICSharpCode.AvalonEdit.Folding.FoldingSection folding = null;

            foreach (var i in this.FoldingManager.AllFoldings)
            {
                if (i.StartOffset <= this.SelectionStart &&
                    i.EndOffset >= this.SelectionStart + this.SelectionLength)
                {
                    if (folding == null)
                    {
                        folding = i;
                        continue;
                    }
                    else
                    {
                        if (folding.StartOffset < i.StartOffset)
                        {
                            folding = i;
                            continue;
                        }
                    }
                }
            }

            if (folding != null)
            {
                folding.IsFolded = !folding.IsFolded;
            }
        }

        /// <summary>
        /// 跳转到下一个单元格。
        /// </summary>
        public bool JumpToNextCell()
        {
            var line = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            if (line == null) return false;
            var text = this.Document.GetText(line.Offset, line.Length);
            if (text.Contains("|") == false && text.Contains("｜") == false) return false;

            if (this.SelectionStart == line.EndOffset && this.SelectionLength == 0)
            {
                if (this.SelectionStart + 2 < this.Text.Length)
                {
                    this.Select(this.SelectionStart + 2, 0);//2是\r\n的长度。
                }
                return true;
            }

            //先在text中查找前一个分割线
            int preIndex = -1;
            for (int i = this.SelectionStart + this.SelectionLength - 1; i >= 0; i--)
            {
                char c = this.Text[i];
                if (c == '|' || c == '｜' || c == '\n' || c == '\r')
                {
                    preIndex = i;
                    break;
                }
            }

            //再找后一个分割线位置
            int nextIndex = -1;
            for (int i = this.SelectionStart + this.SelectionLength; i < this.Text.Length; i++)
            {
                char c = this.Text[i];
                if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                {
                    nextIndex = i;
                    break;
                }
            }

            if (nextIndex > preIndex)
            {
                //如果在首行
                if (line.LineNumber == 1 && preIndex < 0)
                {
                    if (this.SelectionStart != 0 || this.SelectionLength != nextIndex)
                    {
                        this.Select(0, nextIndex);
                    }
                    else
                    {
                        //如果已选中当前单元格所有文本，继续找下一个单元格并选定。
                        var nextIndex2 = -1;
                        for (int i = nextIndex + 1; i < this.Text.Length; i++)
                        {
                            char c = this.Text[i];
                            if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                            {
                                nextIndex2 = i;
                                break;
                            }
                        }

                        if (nextIndex2 < 0)
                        {
                            this.Select(nextIndex + 1, 0);
                        }
                        else if (nextIndex2 > nextIndex)
                        {
                            this.Select(nextIndex + 1, nextIndex2 - nextIndex - 1);
                        }
                    }
                    return true;
                }

                if (Globals.MainWindow.SelectCellFirst &&
                    (this.SelectionStart != preIndex + 1 || this.SelectionLength != nextIndex - preIndex - 1))
                {
                    this.Select(preIndex + 1, nextIndex - preIndex - 1);
                }
                else
                {
                    //如果已选中当前单元格所有文本，继续找下一个单元格并选定。
                    var nextIndex2 = -1;
                    for (int i = nextIndex + 1; i < this.Text.Length; i++)
                    {
                        char c = this.Text[i];
                        if (c == '|' || c == '｜' || c == '\r' || c == '\n')
                        {
                            nextIndex2 = i;
                            break;
                        }
                    }

                    if (nextIndex2 < 0)
                    {
                        this.Select(nextIndex + 1, 0);
                    }
                    else if (nextIndex2 > nextIndex)
                    {
                        this.Select(nextIndex + 1, nextIndex2 - nextIndex - 1);
                    }
                }
                return true;
            }

            return false;
        }

        /// <summary>
        /// 跳转到下一个试题元素。
        /// </summary>
        /// <param name="isShift">Shift 键是否处于被按下状态。</param>
        private bool JumpToNextExamElement(bool isShift)
        {
            if (Globals.MainWindow.IsExamEnabled == false) return false;

            string textToFind = "＞＞[^＞]";
            Regex regex = GetRegEx(textToFind, isShift);
            int start;

            if (isShift)
            {
                var line = this.Document.GetLineByOffset(this.SelectionStart);
                if (line != null)
                {
                    var lineText = this.Document.GetText(line.Offset, line.Length);

                    //找一下，前一个【】在哪里
                    //if (lineText.StartsWith("　　试题＞＞"))
                    //{
                    var startIndex = this.SelectionStart + this.SelectionLength - line.Offset - 1;
                    if (startIndex >= 0)
                    {
                        var rightIndex = lineText.LastIndexOf('】', startIndex);
                        if (rightIndex >= 0)
                        {
                            var leftIndex = lineText.LastIndexOf('【', rightIndex);
                            if (rightIndex > leftIndex)
                            {
                                this.Select(line.Offset + leftIndex + 1, rightIndex - leftIndex - 1);
                                TextLocation loc = this.Document.GetLocation(line.Offset + leftIndex + 1);
                                this.ScrollTo(loc.Line, loc.Column);
                                return true;
                            }
                        }
                    }
                    //}

                    if (lineText.Contains("＞＞"))
                    {
                        start = line.Offset;//跳过这行的开头向文件头部逆向查找，否则只能停留在本行（这样没用处）                        
                    }
                    else
                    {
                        start = regex.Options.HasFlag(RegexOptions.RightToLeft) ? this.SelectionStart : this.SelectionStart + this.SelectionLength;
                    }
                }
                else return false;
            }
            else
            {
                var line = this.Document.GetLineByOffset(this.SelectionStart);
                if (line != null)
                {
                    //正向时，不需要越过标记，所以这里不需要判断本行有无“＞＞”。

                    //找一下，下一个【】在哪里
                    var lineText = this.Document.GetText(line.Offset, line.Length);

                    var startIndex = this.SelectionStart - line.Offset;
                    if (startIndex >= 0)
                    {
                        var leftIndex = lineText.IndexOf('【', startIndex);
                        if (leftIndex >= 0)
                        {
                            var rightIndex = lineText.IndexOf('】', leftIndex);
                            if (rightIndex > leftIndex)
                            {
                                this.Select(line.Offset + leftIndex + 1, rightIndex - leftIndex - 1);
                                TextLocation loc = this.Document.GetLocation(line.Offset + leftIndex + 1);
                                this.ScrollTo(loc.Line, loc.Column);
                                return true;
                            }
                        }
                    }

                    start = line.EndOffset;
                }
                else return false;
            }

            Match match = regex.Match(this.Text, start);

            if (!match.Success)  //重头开始或结束
            {
                if (regex.Options.HasFlag(RegexOptions.RightToLeft))
                    match = regex.Match(this.Text, this.Text.Length);
                else
                    match = regex.Match(this.Text, 0);
            }

            if (match.Success)
            {
                var destLine = this.Document.GetLineByOffset(match.Index + 2);
                int length;
                var destLineText = this.Document.GetText(destLine.Offset, destLine.Length);
                var firstIndex = match.Index - destLine.Offset + 2;
                var lastIndex = destLineText.LastIndexOf("＜＜");
                if (lastIndex >= firstIndex)
                {
                    length = lastIndex - firstIndex;
                }
                else
                {
                    length = destLine.EndOffset - match.Index - 2;
                }

                this.Select(match.Index + 2, length);
                TextLocation loc = this.Document.GetLocation(match.Index);
                this.ScrollTo(loc.Line, loc.Column);
            }

            return match.Success;
        }

        /// <summary>
        /// 根据选项构建正则表达式。
        /// </summary>
        /// <param name="textToFind">要查找的文本</param>
        /// <param name="leftToRight">正序还是逆序。</param>
        private Regex GetRegEx(string textToFind, bool leftToRight)
        {
            RegexOptions options = RegexOptions.None;
            if (leftToRight)
            {
                options |= RegexOptions.RightToLeft;
            }
            options |= RegexOptions.IgnoreCase;

            string pattern = textToFind; // Regex.Escape(textToFind);
            return new Regex(pattern, options);
        }

        /// <summary>
        /// 插入新行。
        /// </summary>
        private void InsertANewLine()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart);
            var text = this.Document.GetText(curLine.Offset, curLine.Length);

            if (text.Contains("|") == false && text.Contains("｜") == false)
            {
                this.Document.Insert(curLine.Offset, "\r\n");
                this.Select(curLine.Offset, 0);
            }
            else
            {
                //如果本行是表格行，仿制一行
                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < text.Length; i++)
                {
                    char c = text[i];
                    if (c == '|')
                    {
                        sb.Append("|");
                    }
                    else if (c == '｜')
                    {
                        sb.Append("｜");
                    }
                    else if ((int)c <= 255)
                    {
                        sb.Append(" ");
                    }
                    else
                    {
                        sb.Append("  ");
                    }
                }

                this.Document.Insert(curLine.Offset, sb.ToString() + "\r\n");
                this.Select(curLine.Offset, 0);
            }
        }

        /// <summary>
        /// 附加新行。
        /// </summary>
        private void AppendANewLine()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var text = this.Document.GetText(curLine.Offset, curLine.Length);

            if (text.Contains("|") == false && text.Contains("｜") == false)
            {
                this.Document.Insert(curLine.EndOffset, "\r\n");
                this.Select(curLine.EndOffset + 2, 0);
            }
            else
            {
                //如果本行是表格行，仿制一行
                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < text.Length; i++)
                {
                    char c = text[i];
                    if (c == '|')
                    {
                        sb.Append("|");
                    }
                    else if (c == '｜')
                    {
                        sb.Append("｜");
                    }
                    else if ((int)c <= 255)
                    {
                        sb.Append(" ");
                    }
                    else
                    {
                        sb.Append("  ");
                    }
                }

                this.Document.Insert(curLine.EndOffset, "\r\n" + sb.ToString());
                this.Select(curLine.EndOffset + 2, 0);
            }
        }

        /// <summary>
        /// 与后一行交换位置。
        /// </summary>
        internal void SwapWithNextLine()
        {
            var curStartLine = this.Document.GetLineByOffset(this.SelectionStart);
            var curEndLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var nextLine = curEndLine.NextLine;

            if (nextLine != null)
            {
                this.BeginChange();
                var lineSplitter = "\n";
                if (this.Document.Text.Contains("\r\n"))
                {
                    lineSplitter = "\r\n";
                }
                var nextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                this.Document.Remove(nextLine.Offset - lineSplitter.Length, nextLine.Length + lineSplitter.Length);//先删除下一行

                var curStartLineOffset = curStartLine.Offset;
                this.Document.Insert(curStartLineOffset, nextLineText + lineSplitter);
                this.EndChange();
            }
        }

        /// <summary>
        /// 与前一行交换位置。
        /// </summary>
        internal void SwapWithPreviewLine()
        {
            var curStartLine = this.Document.GetLineByOffset(this.SelectionStart);
            var curEndLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var preLine = curStartLine.PreviousLine;

            if (preLine != null)
            {
                this.BeginChange();
                var preStartLineOffset = preLine.Offset;
                var startOffset = this.SelectionStart - curStartLine.Offset;
                var selLength = this.SelectionLength;

                var lineSplitter = "\n";
                if (this.Document.Text.Contains("\r\n"))
                {
                    lineSplitter = "\r\n";
                }

                var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                this.Document.Insert(curEndLine.EndOffset, lineSplitter);
                this.Document.Insert(curEndLine.EndOffset + lineSplitter.Length, preLineText);
                this.Document.Remove(preLine.Offset, preLine.Length + lineSplitter.Length);
                this.EndChange();

                this.Select(preStartLineOffset + startOffset, selLength);
            }
        }

        /// <summary>
        /// 寻找前一个标题。
        /// </summary>
        public void FindPreviewHeader()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart);
            if (curLine == null) return;
            int i = curLine.LineNumber - 1;//当前行不算
            DocumentLine preHeaderLine = null;
            while (i > 0)
            {
                var preLine = this.Document.GetLineByNumber(i);
                if (preLine == null) break;

                var text = this.Document.GetText(preLine.Offset, preLine.Length);
                if (text == null) break;

                if (text.Length == 0)
                {
                    i--;
                    continue;
                }
                else
                {
                    Regex reg = new Regex(@"^[ 　#＃]{1,}");
                    if (reg.Match(text).Success)
                    {
                        preHeaderLine = preLine;
                        break;
                    }
                    else
                    {
                        i--;
                        continue;
                    }
                }
            }

            if (preHeaderLine != null)
            {
                this.Select(preHeaderLine.Offset, 0);
                this.ScrollTo(preHeaderLine.LineNumber, 0);
            }
            else
            {
                this.Select(0, 0);
                this.ScrollTo(0, 0);
            }
        }

        /// <summary>
        /// 寻找后一个标题。
        /// </summary>
        public void FindNextHeader()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            if (curLine == null) return;
            int i = curLine.LineNumber + 1;//当前行不算。
            DocumentLine nextHeaderLine = null;
            while (i < this.Document.LineCount)
            {
                var nextLine = this.Document.GetLineByNumber(i);
                if (nextLine == null) break;

                var text = this.Document.GetText(nextLine.Offset, nextLine.Length);
                if (text == null) break;

                if (text.Length == 0)
                {
                    i++;
                    continue;
                }
                else
                {
                    Regex reg = new Regex(@"^[ 　#＃]{1,}");
                    if (reg.Match(text).Success)
                    {
                        nextHeaderLine = nextLine;
                        break;
                    }
                    else
                    {
                        i++;
                        continue;
                    }
                }
            }

            if (nextHeaderLine != null)
            {
                this.Select(nextHeaderLine.Offset, 0);
                this.ScrollTo(nextHeaderLine.LineNumber, 0);
            }
            else
            {
                this.Select(this.Document.Lines[this.Document.Lines.Count - 1].EndOffset, 0);
                this.ScrollToLine(this.Document.Lines.Count - 1);
            }
        }

        /// <summary>
        /// 使当前文本行在“普通文本”与“无序列表项”之间切换。
        /// </summary>
        /// <param name="convertToListItem">作为无序列表项。</param>
        public void SwitchListMark(bool convertToListItem)
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);

            this.BeginChange();

            for (int i = endLine.LineNumber - 1; i >= startLine.LineNumber - 1; i--)
            {
                var line = this.Document.Lines[i];
                var lineText = this.Document.GetText(line.Offset, line.Length);

                var lineContent = lineText.Trim(new char[] { ' ', '　', '\t' });
                if (string.IsNullOrWhiteSpace(lineContent)) continue;

                if (convertToListItem == false)
                {
                    //取消行首的无序列表标记。

                    Regex reg = new Regex(@"^[ 　]{0,3}[\+\-－\*][ 　\t]{0,}");
                    var match = reg.Match(lineText);
                    if (match != null && match.Success)
                    {
                        this.Document.Remove(line.Offset, match.Length);
                    }
                }
                else
                {
                    //在行首添加星号，作为无序列表
                    this.Document.Insert(line.Offset, "+ ");
                }
            }

            this.EndChange();
        }

        /// <summary>
        /// 在选定文本行前加“引用块标记文本（> ）”
        /// </summary>
        internal void AddBlockQuoterMarksToSelectedLines()
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var startReplaceOffset = startLine.Offset;
            var endReplaceOffset = endLine.EndOffset;

            StringBuilder sb = new StringBuilder();
            for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++)
            {
                var line = this.Document.GetLineByNumber(i);
                var lineText = this.Document.GetText(line.Offset, line.Length);
                if (string.IsNullOrWhiteSpace(lineText))
                {
                    sb.Append("\r\n");
                    continue;
                }

                sb.Append("> ");
                sb.Append(lineText);
                sb.Append("\r\n");
            }
            this.Document.Replace(startReplaceOffset, endReplaceOffset - startReplaceOffset + 1, sb.ToString());
        }

        /// <summary>
        /// 在选定文本行前加“代码块标记（四个半角空格）”
        /// </summary>
        internal void AddCodeBlockMarksToSelectedLines()
        {
            try
            {
                var startLine = this.Document.GetLineByOffset(this.SelectionStart);
                var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
                var startReplaceOffset = startLine.Offset;
                var endReplaceOffset = endLine.EndOffset;
                var sNum = startLine.LineNumber;
                var eNum = endLine.LineNumber;

                StringBuilder sb = new StringBuilder();
                for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++)
                {
                    var line = this.Document.GetLineByNumber(i);
                    var lineText = this.Document.GetText(line.Offset, line.Length);
                    if (string.IsNullOrWhiteSpace(lineText))
                    {
                        sb.Append("\r\n");
                        continue;
                    }

                    sb.Append("    ");
                    sb.Append(lineText);
                    sb.Append("\r\n");
                }
                this.Document.Replace(startReplaceOffset, endReplaceOffset - startReplaceOffset, sb.ToString());
                this.Select(Document.GetLineByNumber(sNum).Offset, Document.GetLineByNumber(eNum).EndOffset - startLine.Offset);
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 在选定文本行头部移除“代码块标记（四个半角空格）”
        /// </summary>
        internal void RemoveCodeBlockMarksToSelectedLines()
        {
            try
            {
                var startLine = this.Document.GetLineByOffset(this.SelectionStart);
                var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
                var startReplaceOffset = startLine.Offset;
                var endReplaceOffset = endLine.EndOffset;
                var sNum = startLine.LineNumber;
                var eNum = endLine.LineNumber;

                // 如果每行头部都有最少四个半角空格，则都删除四个；
                // 如果有些行只有不到四个，则都按最少的一行决定删除几个空格。
                int minSpaceCount = int.MaxValue;
                Regex regex = new Regex(@"^[ ]{0,}");
                List<string> lines = new List<string>();
                for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++)
                {
                    var line = this.Document.GetLineByNumber(i);
                    var lineText = this.Document.GetText(line.Offset, line.Length);
                    lines.Add(lineText);
                    if (string.IsNullOrEmpty(lineText)) continue;
                    var match = regex.Match(lineText);
                    if (match.Success == false || match.Length <= 0)
                    {
                        minSpaceCount = 0;
                        break;
                    }

                    minSpaceCount = Math.Min(minSpaceCount, match.Length);
                }

                if (minSpaceCount <= 0) return;

                minSpaceCount = Math.Min(4, minSpaceCount);//每次最多删除四个。

                StringBuilder sb = new StringBuilder();
                foreach (var lt in lines)
                {
                    if (string.IsNullOrWhiteSpace(lt))
                    {
                        sb.Append("\r\n");
                        continue;
                    }

                    if (lt.StartsWith(new string(' ', minSpaceCount)))
                    {
                        sb.Append(lt.Substring(minSpaceCount));
                    }
                    else sb.Append(lt);

                    sb.Append("\r\n");
                }
                this.Document.Replace(startReplaceOffset, endReplaceOffset - startReplaceOffset, sb.ToString());
                this.Select(Document.GetLineByNumber(sNum).Offset, Document.GetLineByNumber(eNum).EndOffset - startLine.Offset);
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 使当前文本行在六级标题之间切换。
        /// </summary>
        /// <param name="newLevel">标题层级。</param>
        internal void SwitchTitleLevel(int newLevel)
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++)
            {
                var line = this.Document.GetLineByNumber(i);
                var lineText = this.Document.GetText(line.Offset, line.Length);
                if (string.IsNullOrWhiteSpace(lineText))
                {
                    var header = GetTitleHeader(newLevel);
                    this.Document.Replace(line.Offset, 0, GetTitleHeader(newLevel));
                    this.Select(line.Offset + header.Length, 0);
                    return;
                }

                var oldSelOffset = this.SelectionStart;
                var oldSelLength = this.SelectionLength;

                Regex reg1 = new Regex(@"^[ 　]{4,}");
                var match1 = reg1.Match(lineText);
                if (match1 != null && match1.Success) return;//不考虑代码块

                Regex reg = new Regex(@"^[ 　#＃]{1,}");
                var match = reg.Match(lineText);
                if (match != null && match.Success)
                {
                    var level = GetTitleLevel(match.Value);
                    if (level == newLevel)
                    {
                        this.Document.Replace(line.Offset, match.Length, "");
                        this.Select(oldSelOffset - match.Length, oldSelLength);
                    }
                    else
                    {
                        var newHeader = GetTitleHeader(newLevel);
                        this.Document.Replace(line.Offset, match.Length, newHeader);
                        var newSelOffset = oldSelOffset + newHeader.Length - match.Length;
                        this.Select(newSelOffset, oldSelLength);
                    }
                }
                else
                {
                    var newHeader = GetTitleHeader(newLevel);
                    this.Document.Insert(line.Offset, GetTitleHeader(newLevel));
                    var newSelOffset = oldSelOffset + newHeader.Length - match.Length;
                    this.Select(newSelOffset, oldSelLength);
                }
            }
        }

        /// <summary>
        /// 取文本行的标题级别。
        /// </summary>
        /// <param name="lineText">文本行。</param>
        private int GetTitleLevel(string lineText)
        {
            if (string.IsNullOrWhiteSpace(lineText)) return 0;
            int level = 0;
            foreach (char c in lineText)
            {
                switch (c)
                {
                    case ' ':
                    case '　': continue;
                    case '＃':
                    case '#':
                        {
                            level++;
                            continue;
                        }
                    default: continue;
                }
            }

            return level;
        }

        /// <summary>
        /// 根据层级生成标题的头文本（#字符串）。。
        /// </summary>
        /// <param name="level">标题层级。</param>
        private string GetTitleHeader(int level)
        {
            switch (level)
            {
                case 1: return "#";
                case 2: return "##";
                case 3: return "###";
                case 4: return "####";
                case 5: return "#####";
                case 6: return "######";
                default: return "";
            }
        }

        /// <summary>
        /// 编辑器获得焦点时自动打开输入法。
        /// </summary>
        void MarkDownEditorBase_GotFocus(object sender, RoutedEventArgs e)
        {
            Globals.SwitchInputMethod(true);
        }

        /// <summary>
        /// 查找面板是否处于开启状态。
        /// * AvalonEdit的查找面板有 Bug，经常无法输入中文。
        /// 所以实际上没有用它，此属性已无用处。
        /// </summary>
        public bool IsSearchPanelOpened
        {
            get
            {
                if (searchPanel == null) return false;

                if (searchPanel.IsClosed) return false;

                return true;
            }
        }

        /// <summary>
        /// [已废弃]AvalonEdit 实现的查找面板，出现在编辑区右上角。有Bug，常无法输入中文。
        /// </summary>
        private SearchPanel searchPanel;

        /// <summary>
        /// [已废弃]关闭 AvalonEdit 自己实现的搜索面板。
        /// </summary>
        public void CloseSearchPanel()
        {
            searchPanel.Close();
        }

        /// <summary>
        /// [已废弃]查找文本。
        /// </summary>
        public void Find()
        {
            searchPanel.SearchPattern = this.TextArea.Selection.GetText();

            if (searchPanel.IsClosed) searchPanel.Open();

            searchPanel.Reactivate();
        }

        /// <summary>
        /// [已废弃]逆向查找。
        /// </summary>
        public void FindPreview()
        {
            searchPanel.FindPrevious();
        }

        /// <summary>
        /// [已废弃]正向查找。
        /// </summary>
        public void FindNext()
        {
            searchPanel.FindNext();
        }

        /// <summary>
        /// 文档内容变化时更改 IsModified 属性的值。
        /// </summary>
        //void MarkdownEditorBase_TextChanged(object sender, EventArgs e)
        //{
        //    this.IsModified = true;//勉强解决。
        //}

        /// <summary>
        /// [已废弃]自动将输入的某些方括号转换为统一格式。
        /// </summary>
        void MarkdownEditorBase_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            //这个转换会造成一些意外的错误——例如添加一个图片链接时需要的不是【】而是[]。
            //switch (e.Text)
            //{
            //    case "[":
            //    case "［":
            //    case "〖":
            //        {
            //            this.Document.Insert(this.CaretOffset, "【");
            //            e.Handled = true;
            //            return;
            //        }
            //    case "]":
            //    case "］":
            //    case "〗":
            //        {
            //            this.Document.Insert(this.CaretOffset, "】");
            //            e.Handled = true;
            //            return;
            //        }
            //}
        }

        #region 自动完成

        /// <summary>
        /// 判断此文本是否与试题相关。
        /// </summary>
        /// <param name="lineText">要检查的文本行。</param>
        public static bool IsStartWithTestPaperKeyWord(string lineText)
        {
            if (string.IsNullOrEmpty(lineText)) return false;

            if (lineText.StartsWith("　　试题＞＞")) return true;
            if (lineText.StartsWith("　　答案＞＞")) return true;
            if (lineText.StartsWith("　　错项＞＞")) return true;
            if (lineText.StartsWith("　　解析＞＞")) return true;
            if (lineText.StartsWith("　　试题＞＞")) return true;
            if (lineText.StartsWith("＜＜材料＞＞")) return true;
            if (lineText.StartsWith("＜＜出处＞＞")) return true;
            if (lineText.StartsWith("＜＜问题＞＞")) return true;
            if (lineText.StartsWith("＜＜＜信息＞")) return true;
            if (lineText.StartsWith("　类型＞＞")) return true;
            if (lineText.StartsWith("　标题＞＞")) return true;
            if (lineText.StartsWith("　日期＞＞")) return true;
            if (lineText.StartsWith("　辑录＞＞")) return true;
            if (lineText.StartsWith("　作者＞＞")) return true;
            if (lineText.StartsWith("　电邮＞＞")) return true;
            if (lineText.StartsWith("　备注＞＞")) return true;
            if (lineText.StartsWith("　辑录＞＞")) return true;
            if (lineText.StartsWith("＜信息＞＞＞")) return true;

            return false;
        }

        private CompletionWindow completionWindow;
        /// <summary>
        /// 自动完成窗口。
        /// </summary>
        public CompletionWindow CompletionWindow
        {
            get
            {
                return completionWindow;
            }
        }

        /// <summary>
        /// 关闭自动完成窗口。
        /// </summary>
        public void InvalidateCompletionWindow()
        {
            if (this.completionWindow == null) return;
            this.completionWindow.Close();
            this.completionWindow = null;
        }

        /// <summary>
        /// 用于在切换工作区时、工作区管理器发生变化时重新载入关于各 Markdown 文件的自动完成列表。
        /// </summary>
        /// <param name="workspaceTreeView">应传入工作区管理器</param>
        public void LoadLinkAutoCompletionItems(TreeView workspaceTreeView, bool isEnableAutoComplete)
        {
            if (this.CompletionWindow == null) return;

            IList<ICompletionData> dataList = CompletionWindow.CompletionList.CompletionData;
            for (int i = dataList.Count - 1; i >= 0; i--)
            {
                var item = dataList[i] as CustomCompletionData;
                if (item == null) continue;

                if (item.Type == CompleteItemType.MarkdownFileLink ||
                    item.Type == CompleteItemType.MetaMarkdownFileLink ||
                    item.Type == CompleteItemType.ImageResourceLink) dataList.RemoveAt(i);
            }//先清除

            if (workspaceTreeView == null || workspaceTreeView.Items.Count <= 0 || isEnableAutoComplete == false) return;

            var wtvi = workspaceTreeView.Items[0] as WorkspaceTreeViewItem;
            if (wtvi != null)
            {
                AddLinksAutoCompleteItem(wtvi, ref dataList);
            }
        }

        /// <summary>
        /// 计算两个文档间的相对位置，即要能从当前文档找到目标链接文档。
        /// </summary>
        /// <param name="thisDocPath">要插入链接的文档的完全路径。</param>
        /// <param name="linkDestDocPath">要被链接的文档的完全路径。</param>
        /// <param name="withHtmlSuffix">true，返回 .html 后缀；false 返回 .md 后缀。</param>
        /// <returns></returns>
        public static string GetRelativePath(string thisDocPath, string linkDestDocPath, bool withHtmlSuffix)
        {
            try
            {
                var thisDocInfo = new FileInfo(thisDocPath);
                var linkDestDocInfo = new FileInfo(linkDestDocPath);

                //用 ~ 代替 _ 风险太高了！！
                //if (linkDestDocInfo.Name.StartsWith("_"))
                //{
                //    linkDestDocPath = linkDestDocInfo.Directory.FullName + "\\~" + linkDestDocInfo.Name.Substring(1);
                //}

                //基本思路：
                //当前文档路径：       C:\abc\def\ghi\jkl\lmn.md
                //要链接到的文件路径： C:\abc\def\mnl\opq\xyz.md
                //办法是先找到两者开始分叉的层级，然后算出当前文档得向上移动几层。

                var parentInfo = thisDocInfo.Directory;
                var root = parentInfo.Root;
                var prefix = "";

                while (parentInfo != null)
                {
                    if (false == linkDestDocPath.StartsWith(parentInfo.FullName, StringComparison.CurrentCultureIgnoreCase))
                    {
                        prefix += "../";
                    }
                    else
                    {
                        //prefix += "../";//这行很奇怪，之前控制台应用程序调试时没问题，但转移到这里却会嫌多。
                        var length = parentInfo.FullName.Length;
                        if (parentInfo.FullName.EndsWith("\\") == false && parentInfo.FullName.EndsWith("/") == false)
                            length++;
                        var tail = linkDestDocPath.Substring(length);  //加的1是指 \
                        var result = (prefix + tail).Replace("\\", "/");
                        if (withHtmlSuffix && result.EndsWith(".md", StringComparison.CurrentCultureIgnoreCase))
                        {
                            result = result.Substring(0, result.Length - 3) + ".html";
                        }

                        return result;
                    }
                    parentInfo = parentInfo.Parent;
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 这里的 Link 包括普通 Markdwon 文件、目录元文件、图像资源文件。
        /// </summary>
        /// <param name="item"></param>
        /// <param name="dataList"></param>
        private void AddLinksAutoCompleteItem(WorkspaceTreeViewItem item, ref IList<ICompletionData> dataList)
        {
            if (item == null || dataList == null) return;
            var editor = this.MasterEditor;
            if (editor == null) return;
            var thisFullPath = editor.FullFilePath;
            if (string.IsNullOrWhiteSpace(thisFullPath)) return;

            var title = item.Title;
            if (item.IsMetaDirectoryPath)
            {
                //添加目录元文件对应的Html文件的路径。
                var metaFilePath = item.MetaHtmlFilePath;
                if (metaFilePath == null) return;

                var relativePath = GetRelativePath(thisFullPath, metaFilePath, true);
                dataList.Add(new CustomCompletionData(title, $"链接到：{title}",
                    $"[{title}]({relativePath} \"{title}\")")
                {
                    Type = CompleteItemType.MetaMarkdownFileLink,
                    Image = linkIcon,
                });
            }
            else if (item.IsMarkdownFilePath)
            {
                //添加Markdown文件对应的Html文件的路径。
                var relativePath = GetRelativePath(thisFullPath, item.FullPath, true);
                dataList.Add(new CustomCompletionData(title, $"链接到：{title}",
                    $"[{title}]({relativePath} \"{title}\")")
                {
                    Type = CompleteItemType.MarkdownFileLink,
                    Image = linkIcon,
                });
            }
            else if (item.IsImageFileExist)
            {
                //添加Markdown文件对应的Html文件的路径。
                var relativePath = GetRelativePath(thisFullPath, item.FullPath, true);
                dataList.Add(new CustomCompletionData(title, $"链接到图像：{title}",
                    $"[{title}]({relativePath} \"{title}\")")
                {
                    Type = CompleteItemType.ImageResourceLink,
                    Image = linkIcon,
                });
            }

            if (item.Items.Count > 0)
            {
                foreach (var si in item.Items)
                {
                    var subItem = si as WorkspaceTreeViewItem;
                    if (subItem == null) continue;
                    AddLinksAutoCompleteItem(subItem, ref dataList);
                }
            }
        }

        /// <summary>
        /// 向自动完成窗口添加可用的条目。
        /// </summary>
        /// <param name="entry">自动完成提示条目。</param>
        public void AddEntryToCompletionWindow(EnToChEntry entry)
        {
            if (this.CompletionWindow == null) return;
            IList<ICompletionData> dataList = CompletionWindow.CompletionList.CompletionData;
            dataList.Add(new CustomCompletionData(entry.Chinese,
                        $"{entry.Chinese}（{entry.Alpha}，{entry.English}）" + (entry.Comment == string.Empty ? null : entry.Comment),
                        $"{entry.Chinese}（{entry.Alpha}，{entry.English}）"));
            dataList.Add(new CustomCompletionData(entry.English,
                        $"{entry.Chinese}（{entry.Alpha}，{entry.English}）" + (entry.Comment == string.Empty ? null : entry.Comment),
                        $"{entry.Chinese}（{entry.Alpha}，{entry.English}）"));
        }

        /// <summary>
        /// 在向活动编辑器中输入文本时，根据情况弹出自动提示。
        /// </summary>
        void TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            if (IsAutoCompletionEnabled == false) return;
            if ("\r\n 　`~!@#$%^&*()_+-=[]\\{}|;\':\",.<>/?～·！＠＃￥％……＆＊（）｛｝｜【】、：＂；＇，．／＜＞？".Contains(e.Text)) return;//仅只换行，不需要提示

            var line = this.Document.GetLineByOffset(this.CaretOffset);
            if (line == null) return;

            if (completionWindow == null)
            {
                completionWindow = new CompletionWindow(this.TextArea)
                {
                    MinHeight = 34,
                    MinWidth = 100,
                };
            }

            IList<ICompletionData> data = CompletionWindow.CompletionList.CompletionData;

            //清除自动完成列表并重新添加其中的项目。
            data.Clear();

            if (IsExamEnabled)//不需要这个，强制换行即可。 && this.CaretOffset - line.Offset <= 3)
            {
                #region 添加试题需要的提示项

                var st = new CustomCompletionData("试题＞＞", "每个试题都应以此开头。", "\r\n　　试题＞＞") { Image = examIcon, };
                var da = new CustomCompletionData("答案＞＞", "每个答案都应以此为开头。", "\r\n　　答案＞＞") { Image = examIcon, };//缩进不采用Tab是因为易乱。
                var cx = new CustomCompletionData("错项＞＞", "每个备选的错项都应以此为开头。", "\r\n　　错项＞＞") { Image = examIcon, };
                var jx = new CustomCompletionData("解析＞＞", "每个解析条目都应以此开头。", "\r\n　　解析＞＞") { Image = examIcon, };
                var cl = new CustomCompletionData("＜＜材料＞＞", "主观题每则材料均应以此开头。", "\r\n＜＜材料＞＞") { Image = examIcon, };
                var cc = new CustomCompletionData("＜＜出处＞＞", "主观题每则材料的出处。", "\r\n＜＜出处＞＞") { Image = examIcon, };
                var wt = new CustomCompletionData("＜＜问题＞＞", "问题集中可包含多个问题。", "\r\n＜＜问题＞＞") { Image = examIcon, };

                var wb = new CustomCompletionData("＝=〓=＝", "最后一道试题的结尾添加此符号才能折叠，其它试题不需要添加。", "〓〓〓〓〓〓") { Image = examIcon, };//这样设计是保证全角等于号与半角等于号都有用。
                var xx = new CustomCompletionData("信息＞＜信息", "每个文档开头应以指明文档相关信息，如章节、创建者、创建时间等等。",
                    "＜＜＜信息＞\r\n" +
                    "　类型＞＞＜＜类型\r\n" +
                    "　标题＞＞请输入文档标题＜＜标题\r\n" +
                    "　日期＞＞＜＜日期\r\n" +
                    "　辑录＞＞＜＜辑录\r\n" +
                    "　电邮＞＞＜＜电邮\r\n" +
                    "　备注＞＞＜＜备注\r\n" +
                    "＜信息＞＞＞\r\n\r\n")
                { Image = documentInfoIcon, };

                //st.Completed += st_Completed;
                //da.Completed += Item_Completed;
                //cx.Completed += Item_Completed;
                //jx.Completed += Item_Completed;
                //cl.Completed += Item_Completed;
                //cc.Completed += Item_Completed;
                //wt.Completed += Item_Completed;
                //wb.Completed += Item_Completed;
                xx.Completed += xx_Completed;

                data.Add(st);
                data.Add(da);
                data.Add(cx);
                data.Add(jx);
                data.Add(cl);
                data.Add(cc);
                data.Add(wt);
                //data.Add(new MyCompletionData("【方括弧对】", "试题中应包含一个或多个填空项目，这些填空项用一对方括弧包围。","【】"));//效果不好。
                data.Add(wb);
                data.Add(xx);

                #endregion
            }

            #region 添加水平线相关的几个项

            var spx1 = new CustomCompletionData("水平线1", "水平线 01", "\r\n<hr class=\"hr1\"/>\r\n") { Image = horizontalLineIcon, };
            var spx2 = new CustomCompletionData("水平线2", "水平线 02", "\r\n<hr class=\"hr2\"/>\r\n") { Image = horizontalLineIcon, };
            var spx3 = new CustomCompletionData("水平线3", "水平线 03", "\r\n<hr class=\"hr3\"/>\r\n") { Image = horizontalLineIcon, };
            var spx4 = new CustomCompletionData("水平线4", "水平线 04", "\r\n<hr class=\"hr4\"/>\r\n") { Image = horizontalLineIcon, };
            var spx5 = new CustomCompletionData("水平线5", "水平线 05", "\r\n<hr class=\"hr5\"/>\r\n") { Image = horizontalLineIcon, };

            data.Add(spx1);
            data.Add(spx2);
            data.Add(spx3);
            data.Add(spx4);
            data.Add(spx5);

            #endregion

            #region 添加几个内置环境变量

            var gVTitle = new CustomCompletionData("/*Title*/", "[环境变量]引用当前文档的标题文本。", "/*Title*/");
            var gVFileName = new CustomCompletionData("/*FileName*/", "[环境变量]引用当前文件的短名。", "/*FileName*/");
            var gVPath = new CustomCompletionData("/*Path*/", "[环境变量]引用当前文件的路径。", "/*Path*/");
            var gVFullName = new CustomCompletionData("/*FullName*/", "[环境变量]引用当前文件的路径。", "/*FullName*/");
            var gVFullPath = new CustomCompletionData("/*FullPath*/", "[环境变量]引用当前文件的路径。", "/*FullPath*/");

            var gVDirectoryName = new CustomCompletionData("/*DirectoryName*/", "[环境变量]引用当前文件所在的目录的短名。", "/*DirectoryName*/");
            var gVDirectoryFullName = new CustomCompletionData("/*DirectoryFullName*/", "[环境变量]引用当前文件所在的目录的路径。", "/*DirectoryFullName*/");
            var gVDirectoryPath = new CustomCompletionData("/*DirectoryPath*/", "[环境变量]引用当前文件所在的目录的路径。", "/*DirectoryPath*/");
            var gVDirectoryFullPath = new CustomCompletionData("/*DirectoryFullPath*/", "[环境变量]引用当前文件所在的目录的路径。", "/*DirectoryFullPath*/");
            var gVWorkspaceTitle = new CustomCompletionData("/*WorkspaceTitle*/", "[环境变量]引用当前文件所在工作区元文件的标题文本。", "/*WorkspaceTitle*/");

            var gVWorkspaceName = new CustomCompletionData("/*WorkspaceName*/", "[环境变量]引用当前工作区目录的短名。", "/*WorkspaceName*/");
            var gVWorkspacePath = new CustomCompletionData("/*WorkspacePath*/", "[环境变量]引用当前工作区目录的路径。", "/*WorkspacePath*/");
            var gVWorkspaceFullName = new CustomCompletionData("/*WorkspaceFullName*/", "[环境变量]引用当前工作区目录的路径。", "/*WorkspaceFullName*/");
            var gVWorkspaceFullPath = new CustomCompletionData("/*WorkspaceFullPath*/", "[环境变量]引用当前工作区目录的路径。", "/*WorkspaceFullPath*/");
            var gVRelativePath = new CustomCompletionData("/*RelativePath*/", "[环境变量]引用当前文档相对于工作区目录的路径。", "/*RelativePath*/");

            var gVPageHeader = new CustomCompletionData("/*PageHeader*/", "[环境变量]引用当前文档的页眉文本。", "/*PageHeader*/");
            var gVPageFooter = new CustomCompletionData("/*PageFooter*/", "[环境变量]引用当前文档的页脚文本。", "/*PageFooter*/");
            var gVNav = new CustomCompletionData("/*Nav*/", "[环境变量]在当前位置生成指向各上级目录节点元文件的导航链接列表。", "/*Nav*/");
            var gVSubLinksList = new CustomCompletionData("/*SubLinksList*/", "[环境变量]在当前位置生成指向当前节点直接下级的目录、文件的链接列表。", "/*SubLinksList*/");

            data.Add(gVTitle);
            data.Add(gVFileName);
            data.Add(gVPath);
            data.Add(gVFullName);
            data.Add(gVFullPath);

            data.Add(gVDirectoryName);
            data.Add(gVDirectoryFullName);
            data.Add(gVDirectoryPath);
            data.Add(gVDirectoryFullPath);
            data.Add(gVWorkspaceTitle);

            data.Add(gVWorkspaceName);
            data.Add(gVWorkspacePath);
            data.Add(gVWorkspaceFullName);
            data.Add(gVWorkspaceFullPath);
            data.Add(gVRelativePath);

            data.Add(gVPageHeader);
            data.Add(gVPageFooter);
            data.Add(gVNav);
            data.Add(gVSubLinksList);

            //添加用户环境变量
            foreach (var d in Globals.MainWindow.UserEnvironmentValues)
            {
                data.Add(new CustomCompletionData($"/*{d.Key}*/", "[用户环境变量]", $"/*{d.Key}*/"));
            }

            //添加工和区环境变量
            foreach (var d in Globals.MainWindow.WorkspaceEnvironmentValues)
            {
                data.Add(new CustomCompletionData($"/*{d.Key}*/", "[工作区环境变量]", $"/*{d.Key}*/"));
            }
            #endregion

            #region 添加指定Markdown元素

            var anchor = new CustomCompletionData("锚", "锚", "\r\n[](@请输入锚的标识符)\r\n") { Image = anchorIcon, };
            anchor.Completed += Anchor_Completed;
            data.Add(anchor);

            var region = new CustomCompletionData("折叠区", "自定义折叠区域", "\r\n\r\nregion { 请输入折叠区标题\r\n\r\n\r\n\r\n}region \r\n\r\n") { Image = collapseAreaIcon, };
            region.Completed += Region_Completed;
            data.Add(region);

            var link = new CustomCompletionData("链接符", "适用于链接工作区以外的地址", "[请输入链接名称]()") { Image = emptyLinkIcon, };
            link.Completed += Link_Completed;
            data.Add(link);

            #endregion

            #region 添加英译中词典

            if (IsEnToChineseDictEnabled)
            {
                //new CustomCompletionData("锚", "锚", "\r\n[](@请输入锚的标识符)\r\n");
                foreach (var entry in Globals.MainWindow.Dictionary)
                {
                    data.Add(new CustomCompletionData(entry.Chinese,
                                $"{entry.Chinese}（{entry.Alpha}，{entry.English}）" + (entry.Comment == string.Empty ? null : entry.Comment),
                                $"{entry.Chinese}（{entry.Alpha}，{entry.English}）")
                    { Image = dictionEntryIcon, });
                    data.Add(new CustomCompletionData(entry.English,
                                $"{entry.Chinese}（{entry.Alpha}，{entry.English}）" + (entry.Comment == string.Empty ? null : entry.Comment),
                                $"{entry.Chinese}（{entry.Alpha}，{entry.English}）")
                    { Image = dictionEntryIcon, });
                }
            }

            #endregion

            #region 添加对工作区中各文件的链接
            LoadLinkAutoCompletionItems(Globals.MainWindow.tvWorkDirectory, Globals.MainWindow.IsAutoCompletionEnabled);
            #endregion

            CompletionWindow.Show();
            CompletionWindow.Closed += delegate
            {
                completionWindow = null;
            };
        }

        private static ImageSource documentInfoIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/document-info.png"));
        private static ImageSource examIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/options.png"));
        private static ImageSource horizontalLineIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/insert-horizontal-rule.png"));
        private static ImageSource anchorIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/link.png"));
        private static ImageSource collapseAreaIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/collapseArea.png"));
        private static ImageSource emptyLinkIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/link-edit.png"));
        private static ImageSource dictionEntryIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/book-key.png"));
        private static ImageSource linkIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/AutoCompleteIcons/document-link.png"));


        /// <summary>
        /// 自动完成链接文本标记。
        /// </summary>
        private void Link_Completed(object sender, EventArgs e)
        {
            this.Select(this.SelectionStart - 10, 7);
        }

        /// <summary>
        /// 自动完成“region”文本标记。
        /// </summary>
        private void Region_Completed(object sender, EventArgs e)
        {
            this.Select(this.SelectionStart - 28, 8);
        }

        /// <summary>
        /// 自动完成锚文本标记。
        /// </summary>
        private void Anchor_Completed(object sender, EventArgs e)
        {
            this.Select(this.SelectionStart - 11, 8);
        }

        /// <summary>
        /// 自动完成“信息”文本标记。
        /// </summary>
        void xx_Completed(object sender, EventArgs e)
        {
            this.Select(this.SelectionStart - 67, 7);
        }

        /// <summary>
        /// 自动完成（上屏）。
        /// </summary>
        void TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            if (IsAutoCompletionEnabled == false) return;
            if (CompletionWindow == null) return;

            if (e.Text.Length > 0)
            {
                if (CompletionWindow.CompletionList.SelectedItem == null)
                {
                    CompletionWindow.Close();
                    completionWindow = null;
                    return;
                }

                if (!char.IsLetterOrDigit(e.Text[0]))
                {
                    if (e.Text[0] != '=' && e.Text[0] != '＝')//不屏蔽等号会导致一闪而过，虽然方便，但会难以理解。
                    {
                        // Whenever a non-letter is typed while the completion window is open,
                        // insert the currently selected element.
                        CompletionWindow.CompletionList.RequestInsertion(e);
                    }
                }
            }

            // Do not set e.Handled=true.
            // We still want to insert the character that was typed.
        }

        #endregion

        #region 代码折叠

        void foldingUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (foldingStrategy != null)
            {
                foldingStrategy.UpdateFoldings(FoldingManager, this.Document);
            }
        }

        DispatcherTimer foldingUpdateTimer = new DispatcherTimer();

        /// <summary>
        /// 提供这个属性是为了便于在选项卡“不在最前”时停止折叠计算，减少消耗。
        /// </summary>
        public DispatcherTimer FoldingUpdateTimer { get { return this.foldingUpdateTimer; } }

        public CustomFoldingManager FoldingManager
        {
            get { return foldingManager; }
        }

        CustomFoldingManager foldingManager;
        AbstractFoldingStrategy foldingStrategy;

        public AbstractFoldingStrategy FoldingStrategy
        {
            get { return foldingStrategy; }
        }

        #endregion

        // <summary>
        // 以选择区起始位置为准，选择所在的整行文本。
        // </summary>
        internal void SelectLine()
        {
            if (this.SelectionStart >= 0 && this.SelectionStart <= this.Document.TextLength)
            {
                var line = this.Document.GetLineByOffset(this.SelectionStart);
                if (line != null)
                {
                    if (this.SelectionStart != line.Offset && this.SelectionLength != line.Length)
                    {
                        this.Select(line.Offset, line.EndOffset - line.Offset);
                    }
                    else
                    {
                        if (line.Length >= 6)
                        {
                            var header = this.Document.GetText(line.Offset, 6);
                            var reg = new Regex(@"　{2,2}(试题|答案|错项|解析)＞{2,}");
                            if (reg.Match(header).Success)
                            {
                                this.Select(line.Offset + 6, line.EndOffset - line.Offset - 6);
                            }
                        }
                    }
                }
            }
        }

        #region 交换上下相邻行，有更好的方案，此方案因只支持单行，被废弃
        //internal void SwapToPreviewLine()
        //{
        //    if (this.SelectionLength > 0) return;

        //    var line = this.Document.GetLineByOffset(this.SelectionStart);
        //    if (line.LineNumber <= 1) return;

        //    int caretOffset = this.SelectionStart - line.Offset;

        //    var previewLine = this.Document.GetLineByNumber(line.LineNumber - 1);

        //    string previewText = this.Document.GetText(previewLine.Offset, previewLine.Length);
        //    string thisText = this.Document.GetText(line.Offset, line.Length);

        //    this.Document.Remove(previewLine.Offset, previewLine.Length);
        //    this.Document.Remove(line.Offset, line.Length);

        //    int previewOffset = this.Document.GetLineByNumber(previewLine.LineNumber).Offset;
        //    this.Document.Insert(previewOffset, thisText);

        //    int thisOffset = this.Document.GetLineByNumber(line.LineNumber).Offset;
        //    this.Document.Insert(thisOffset, previewText);

        //    this.Select(previewOffset + caretOffset, 0);
        //}

        //internal void SwapToNextLine()
        //{
        //    if (this.SelectionLength > 0) return;

        //    var line = this.Document.GetLineByOffset(this.SelectionStart);
        //    if (line.LineNumber >= this.Document.LineCount) return;

        //    int caretOffset = this.SelectionStart - line.Offset;

        //    var nextLine = this.Document.GetLineByNumber(line.LineNumber + 1);

        //    string nextText = this.Document.GetText(nextLine.Offset, nextLine.Length);
        //    string thisText = this.Document.GetText(line.Offset, line.Length);

        //    this.Document.Remove(nextLine.Offset, nextLine.Length);
        //    this.Document.Remove(line.Offset, line.Length);

        //    int thisOffset = this.Document.GetLineByNumber(line.LineNumber).Offset;
        //    this.Document.Insert(thisOffset, nextText);

        //    int nextOffset = this.Document.GetLineByNumber(nextLine.LineNumber).Offset;
        //    this.Document.Insert(nextOffset, thisText);

        //    this.Select(nextOffset + caretOffset, 0);
        //} 
        #endregion

        /// <summary>
        /// 用一对反引号包围选定的文本片段（不能跨行）。
        /// </summary>
        internal void WrapTextWithAntiQuotes()
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            if (startLine.LineNumber != endLine.LineNumber) return;

            if (this.SelectionLength > 0)
            {
                var oldSelLength = this.SelectionLength;

                this.SelectedText = "`" + this.SelectedText + "`";

                this.Select(this.SelectionStart + 1, oldSelLength);
            }
            else
            {
                this.SelectedText = "`" + this.SelectedText + "`";
                this.Select(this.SelectionStart + 1, 0);
            }
        }

        /// <summary>
        /// 用一对引号包围选定的文本片段（不能跨行）。
        /// </summary>
        internal void WrapTextWithQuotes(bool isShift)
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            if (startLine.LineNumber != endLine.LineNumber) return;

            if (this.SelectionLength > 0)
            {
                var oldSelLength = this.SelectionLength;

                if (isShift)
                {
                    this.SelectedText = "“" + this.SelectedText + "”";
                }
                else
                {
                    this.SelectedText = "‘" + this.SelectedText + "’";
                }
                this.Select(this.SelectionStart + 1, oldSelLength);
            }
            else
            {
                if (isShift)
                {
                    this.SelectedText = "“" + this.SelectedText + "”";
                }
                else
                {
                    this.SelectedText = "‘" + this.SelectedText + "’";
                }
                this.Select(this.SelectionStart + 1, 0);
            }
        }

        /// <summary>
        /// 用方括号对【】包围选定的文本片段（不能跨行）。
        /// </summary>
        internal void WrapTextWithSquareQuotes()
        {
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            if (startLine.LineNumber != endLine.LineNumber) return;

            if (this.SelectionLength > 0)
            {
                this.SelectedText = "【" + this.SelectedText + "】";
            }
            else
            {
                this.SelectedText = "【" + this.SelectedText + "】";
                this.Select(this.SelectionStart + 1, 0);
            }
        }

        /// <summary>
        /// 尝试根据试题文本判断其中有几个填空项，然后根据这些填空项自动插入一些“答案＞＞”“错项＞＞”“解析＞＞”之类的标签。
        /// </summary>
        public void InsertChoiceQuestionTags()
        {
            //选择区域所在的行都选中
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            this.Select(startLine.Offset, endLine.EndOffset - startLine.Offset);

            //要求选中题干文本（全部）
            var text = this.SelectedText.Replace("\r\n", "").Replace("\t", "").Replace(" ", "").Replace("　", "");
            if (text.StartsWith("试题＞＞") == false)
            {
                LMessageBox.Show("请选中要生成标签的试题文本，这些文本中应至少有一个用方括弧对标记出来的填充项。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            int fillblankCount = 0;
            StringBuilder errorMsg = new StringBuilder();

            if (QuestionValidateManager.ValidatePairOfBrackets(text, ref fillblankCount, true, errorMsg) == false)
            {
                LMessageBox.Show("选中的文本格式存在问题，无法自动生成标签集。错误信息如下：\r\n" + errorMsg.ToString(),
                   Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            char[] splitterLeft = new char[1];
            splitterLeft[0] = '【';

            char[] splitterRight = new char[1];
            splitterRight[0] = '】';

            string[] arrayLeft = text.Split(splitterLeft, StringSplitOptions.RemoveEmptyEntries);

            List<string> answers = new List<string>();

            foreach (string s in arrayLeft)
            {
                if (s.Contains("】") == false) continue;

                int index = s.IndexOf('】');
                if (index < 0) continue;

                string answer = s.Substring(0, index);
                answers.Add(answer);
            }

            StringBuilder tagsText = new StringBuilder();

            int fstAnswerLength = -1;

            tagsText.Append("\r\n\t\t\t\r\n");
            foreach (var s in answers)
            {
                if (fstAnswerLength < 0)
                {
                    fstAnswerLength = s.Length;
                }

                tagsText.Append("　　答案＞＞" + s + "\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");

                //默认三个错项。
                tagsText.Append("　　错项＞＞\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");
                tagsText.Append("　　错项＞＞\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");
                tagsText.Append("　　错项＞＞\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");
            }
            tagsText.Append("〓〓〓〓〓〓\r\n\r\n");//空一行，顶格，方便立即开始编辑新试题

            var line = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength - 1);//这个长度肯定大于0。

            int oldEndOffset = line.EndOffset;

            this.Document.Insert(line.EndOffset, tagsText.ToString());
            this.Select(oldEndOffset + fstAnswerLength + 34, 0);
        }

        /// <summary>
        /// 添加判断题标记。
        /// </summary>
        /// <param name="judgeValue">判断题答案标记。</param>
        internal void InsertJudgeQuestionTags(string judgeValue = "正确")
        {
            //选择区域所在的行都选中
            var startLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            this.Select(startLine.Offset, endLine.EndOffset - startLine.Offset);

            //要求选中题干文本（全部）
            var text = this.SelectedText.Replace("\r\n", "").Replace("\t", "").Replace(" ", "").Replace("　", "");
            if (text.StartsWith("试题＞＞") == false)
            {
                LMessageBox.Show("请选中要生成标签的试题文本，这些文本中不应包括方括弧【和】，因为方括弧对包括的文本会被当成填充项。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (text.Contains("【") || text.Contains("】"))
            {
                LMessageBox.Show("选中的文本格式存在问题，无法自动生成判断题标签集。错误信息如下：\r\n" +
                    "判断题题干中不应包括方括号【和】，被方括号包围的文本会被当成填空项来处理。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //在末尾加上【正确】或【错误】。
            text += "【" + judgeValue + "】";

            char[] splitterLeft = new char[1];
            splitterLeft[0] = '【';

            char[] splitterRight = new char[1];
            splitterRight[0] = '】';

            string[] arrayLeft = text.Split(splitterLeft, StringSplitOptions.RemoveEmptyEntries);

            List<string> answers = new List<string>();

            foreach (string s in arrayLeft)
            {
                if (s.Contains("】") == false) continue;

                int index = s.IndexOf('】');
                if (index < 0) continue;

                string answer = s.Substring(0, index);
                answers.Add(answer);
            }

            StringBuilder tagsText = new StringBuilder();

            int fstAnswerLength = -1;

            tagsText.Append("【" + judgeValue + "】\r\n\t\t\t\r\n");
            foreach (var s in answers)
            {
                if (fstAnswerLength < 0)
                {
                    fstAnswerLength = s.Length;
                }

                tagsText.Append("　　答案＞＞" + judgeValue + "\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");

                //默认三个错项。
                tagsText.Append("　　错项＞＞" + (judgeValue == "正确" ? "错误" : "正确") + "\r\n");
                tagsText.Append("　　解析＞＞\r\n\t\t\t\r\n");
            }
            tagsText.Append("〓〓〓〓〓〓\r\n\r\n");//空一行，顶格，方便立即开始编辑新试题

            var line = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength - 1);//这个长度肯定大于0。

            int oldEndOffset = line.EndOffset;

            this.Document.Insert(line.EndOffset, tagsText.ToString());
            this.Select(oldEndOffset + fstAnswerLength + 25, 0);
        }

        /// <summary>
        /// 格式化 Markdown 文本。
        /// </summary>
        public void Format()
        {
            var selStart = this.SelectionStart;
            var selLength = this.SelectionLength;

            this.Document.Text = CustomMarkdownSupport.FormatMarkdownText(this.Text);

            //如果开启了演讲者模式，格式化后自动折叠特殊折叠块。
            if (Globals.MainWindow.PerspectiveMode == Perspective.EditingAndPresentation)
            {
                this.FoldingManager.FoldSpecialFoldings = true;
                this.FoldingStrategy.UpdateFoldings(this.FoldingManager, this.Document);
                this.FoldingManager.FoldSpecialFoldings = false;
            }

            if (selStart < this.Document.TextLength)
            {
                if (selStart + selLength < this.Document.TextLength)
                {
                    this.Select(selStart, selLength);
                    this.ScrollToLine(this.Document.GetLineByOffset(this.SelectionStart).LineNumber);
                }
                else
                {
                    this.Select(selStart, 0);
                    this.ScrollToLine(this.Document.GetLineByOffset(this.SelectionStart).LineNumber);
                }
            }
            else
            {
                this.Select(this.Document.TextLength, 0);
                this.ScrollToLine(this.Document.GetLineByOffset(this.SelectionStart).LineNumber);
            }
        }

        public void FormatTextTable()
        {
            var curLine = CurrentDocumentLine();
            var curLineText = this.Document.GetText(curLine.Offset, curLine.Length);

            if (CustomMarkdownSupport.IsTreeListTextLine(curLineText))
            {
                //如果当前行是树型列表行，则应格式化当前行前、后可能存在的树型列表行。

                List<TreeListTextLine> allLines = new List<TreeListTextLine>();

                //向前找
                List<TreeListTextLine> preLines = new List<TreeListTextLine>();

                var preLine = curLine.PreviousLine;
                while (preLine != null)
                {
                    var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                    if (CustomMarkdownSupport.IsTreeListTextLine(preLineText) == false) break;

                    string preHeader, preTail; int preLevel;
                    if (IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel))
                    {
                        preLines.Insert(0, new TreeListTextLine()
                        {
                            Line = preLine,
                            OldText = preLineText,
                            NewText = "",
                            //HeaderTextArray = preHeader.ToCharArray(),
                            TailText = preTail,
                            OldLevel = preLevel,
                            NewLevel = preLevel,   //之前的行层级不变
                        });
                    }

                    preLine = preLine.PreviousLine;
                }

                allLines.AddRange(preLines);

                string curHeader, curTail; int curLevel;
                if (IsTreeListLine(curLineText, out curHeader, out curTail, out curLevel))
                {
                    allLines.Add(new TreeListTextLine()
                    {
                        Line = curLine,
                        OldText = curLineText,
                        TailText = curTail,
                        OldLevel = curLevel,
                        NewLevel = curLevel,
                        NewText = "",
                    });
                }

                //向后找
                List<TreeListTextLine> nextLines = new List<TreeListTextLine>();

                var nextLine = curLine.NextLine;
                var preLevelOfNextLines = curLevel;
                while (nextLine != null)
                {
                    var nextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                    if (CustomMarkdownSupport.IsTreeListTextLine(nextLineText) == false) break;
                    string nextHeader, nextTail; int nextLevel;
                    if (IsTreeListLine(nextLineText, out nextHeader, out nextTail, out nextLevel))
                    {
                        var nextTreeListTextLine = new TreeListTextLine()
                        {
                            Line = nextLine,
                            //HeaderTextArray = nextHeader.ToCharArray(),
                            NewText = "",
                            OldText = nextLineText,
                            TailText = nextTail,
                            OldLevel = nextLevel,
                        };

                        if (nextTreeListTextLine.OldLevel > preLevelOfNextLines + 1)
                        {
                            nextTreeListTextLine.NewLevel = preLevelOfNextLines + 1;
                        }
                        else nextTreeListTextLine.NewLevel = nextTreeListTextLine.OldLevel;//不变

                        nextLines.Add(nextTreeListTextLine);
                        nextLine = nextLine.NextLine;
                    }
                }

                allLines.AddRange(nextLines);

                var oldSelStart = this.SelectionStart;
                FormatAllTreeLines(allLines, null);
                if (oldSelStart < this.Document.TextLength)
                {
                    this.Select(oldSelStart, 0);
                }

                return;
            }
            else
            {
                if (curLineText.Contains("｜") || curLineText.Contains("|"))
                {
                    //这种情况下格式化为二维文字表。
                    Format2DTextTable();
                }
                else
                {
                    //如果当前行是按Shift+Backspace取消的，则要更新当前行上、下可能存在的树型文字表
                    //这种情况要分别格式化成两个树型文字表。

                    //向前找
                    List<TreeListTextLine> preLines = new List<TreeListTextLine>();

                    var preLine = curLine.PreviousLine;
                    while (preLine != null)
                    {
                        var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                        if (CustomMarkdownSupport.IsTreeListTextLine(preLineText) == false) break;

                        string preHeader, preTail; int preLevel;
                        if (IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel))
                        {
                            preLines.Insert(0, new TreeListTextLine()
                            {
                                Line = preLine,
                                OldText = preLineText,
                                NewText = "",
                                //HeaderTextArray = preHeader.ToCharArray(),
                                TailText = preTail,
                                OldLevel = preLevel,
                                NewLevel = preLevel,   //之前的行层级不变
                            });
                        }

                        preLine = preLine.PreviousLine;
                    }

                    FormatAllTreeLines(preLines, null);

                    //向后找
                    List<TreeListTextLine> nextLines = new List<TreeListTextLine>();

                    var nextLine = curLine.NextLine;
                    while (nextLine != null)
                    {
                        var nextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                        if (CustomMarkdownSupport.IsTreeListTextLine(nextLineText) == false) break;
                        string nextHeader, nextTail; int nextLevel;
                        if (IsTreeListLine(nextLineText, out nextHeader, out nextTail, out nextLevel))
                        {
                            var nextTreeListTextLine = new TreeListTextLine()
                            {
                                Line = nextLine,
                                //HeaderTextArray = nextHeader.ToCharArray(),
                                NewText = "",
                                OldText = nextLineText,
                                TailText = nextTail,
                                OldLevel = nextLevel - 1,
                            };
                            nextLines.Add(nextTreeListTextLine);
                        }

                        nextLine = nextLine.NextLine;
                    }

                    FormatAllTreeLines(nextLines, null);
                }
            }
        }

        /// <summary>
        /// 格式化二维文字表。
        /// </summary>
        /// <param name="insertColumnIndex">可以指定文字表的列数。</param>
        private void Format2DTextTable(int? insertColumnIndex = null)
        {
            try
            {
                var curLine = this.Document.GetLineByOffset(this.SelectionStart);
                var curLineNumber = curLine.LineNumber;
                int selStartOffset = this.SelectionStart - curLine.Offset;

                this.Document.UndoStack.StartUndoGroup();

                int startOffset;
                int endOffset;
                string commentText;
                var srcTableText = FindSourceTableText(out startOffset, out endOffset, out commentText);

                if (string.IsNullOrEmpty(srcTableText)) return;

                var lines = new List<TableLine>();

                var srcLines = srcTableText.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string s in srcLines)
                {
                    if (IsTableCaptionSplittor(s)) continue;//忽略“表格格式化”功能自动添加的标题与标题分割行。
                    if (s.StartsWith(";") || s.StartsWith("；")) continue;//忽略备注

                    if (s.EndsWith("｜^") || s.EndsWith("|^"))
                    {
                        lines.Add(new TableLine() { LineText = s.Substring(0, s.Length - 1), Type = TableLineType.MergeLine, });
                    }
                    else
                    {
                        lines.Add(new TableLine() { LineText = s, Type = TableLineType.Normal, });
                    }
                }

                if (lines.Count <= 0) return;

                var cellSplittor = new char[] { '｜' };
                var maxColumn = 0;

                var lineArrays = new List<TableCellArray>();
                string[] columnDefinitionArray = null;

                var columnDefinitionLineIndex = -1;
                var removedColumnDefinitionLinesCount = 0;
                var columnDefinitionLineFinded = false;
                for (int i = 0; i < lines.Count; i++)
                {
                    var text = lines[i].LineText;
                    text = text.Replace("|", "｜").Trim();
                    if (text.StartsWith("｜", StringComparison.CurrentCulture)) text = text.Substring(1);
                    if (text.EndsWith("｜", StringComparison.CurrentCulture)) text = text.Substring(0, text.Length - 1);

                    var cells = text.Split(cellSplittor, StringSplitOptions.None);
                    for (int j = 0; j < cells.Length; j++)
                    {
                        cells[j] = cells[j].Trim();
                    }

                    if (CustomMarkdownSupport.IsColumnAlignmentDefinitionLine("｜" + text + "｜"))
                    {
                        columnDefinitionArray = cells;
                        if (columnDefinitionLineFinded == false)
                        {
                            columnDefinitionLineIndex = i;
                            columnDefinitionLineFinded = true;
                        }
                        removedColumnDefinitionLinesCount++;
                    }
                    else
                    {
                        lineArrays.Add(new TableCellArray() { Cells = cells, Type = lines[i].Type, });
                    }

                    lines[i].LineText = text;
                    maxColumn = Math.Max(cells.Length, maxColumn);
                }

                var columnDefinitions = new List<TableColumnDefinition>();
                for (int i = 0; i < maxColumn; i++)
                {
                    columnDefinitions.Add(new TableColumnDefinition());
                }

                //if (columnDefinitions.Count <= 0 || lineArrays.Count <= 0)
                //{
                //    //LMessageBox.Show("插入附近似乎没有可以格式化的表格！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                //    //return;

                //    for (int i = 0; i < maxColumn; i++)
                //    {
                //        columnDefinitions.Add(new TableColumnDefinition());
                //    }
                //}

                #region 取各列对齐方式定义
                bool hascolumndefinitions = false;
                if (columnDefinitionArray != null && columnDefinitionArray.Length > 0)
                {
                    hascolumndefinitions = true;
                    for (int i = 0; i < maxColumn; i++)
                    {
                        if (i < columnDefinitionArray.Length)
                        {
                            columnDefinitionArray[i] = columnDefinitionArray[i].Replace("：", ":").Trim();

                            if (columnDefinitionArray[i].Contains("^"))
                            {
                                columnDefinitions[i].StartNumber = 0;
                            }

                            if (columnDefinitionArray[i].StartsWith(":", StringComparison.CurrentCulture) ||
                                columnDefinitionArray[i].StartsWith("^:", StringComparison.CurrentCulture))
                            {
                                columnDefinitions[i].Align =
                                    (columnDefinitionArray[i].EndsWith(":", StringComparison.CurrentCulture) ||
                                    columnDefinitionArray[i].EndsWith(":^", StringComparison.CurrentCulture)) ?
                                    ColumnAlignment.CENTER : ColumnAlignment.LEFT;
                            }
                            else if (columnDefinitionArray[i].EndsWith(":", StringComparison.CurrentCulture) ||
                                columnDefinitionArray[i].EndsWith(":^", StringComparison.CurrentCulture))
                            {
                                columnDefinitions[i].Align = ColumnAlignment.RIGHT;
                            }
                            else
                            {
                                columnDefinitions[i].Align = ColumnAlignment.LEFT;
                            }
                        }
                        else
                        {
                            columnDefinitions[i].Align = ColumnAlignment.LEFT;
                        }
                    }
                }
                #endregion

                #region 取各列最大字符宽度
                for (int i = 0; i < lineArrays.Count; i++)
                {
                    var cells = lineArrays[i];

                    for (int j = 0; j < maxColumn; j++)
                    {
                        if (j == 0 && i == 0 && cells.Cells.Length == 1 && hascolumndefinitions)
                        {
                            //第一列需要考虑标题的长度，不能把表格标题的长度作为第一列所需要的最大宽度
                            continue;
                        }

                        if (j < cells.Cells.Length)
                        {
                            columnDefinitions[j].MaxTextWidth = Math.Max(GetTextWidth(cells.Cells[j]), columnDefinitions[j].MaxTextWidth);
                        }
                    }

                    for (int j = 0; j < columnDefinitions.Count; j++)
                    {
                        if (columnDefinitions[j].MaxTextWidth % 2 == 1)
                        {
                            columnDefinitions[j].MaxTextWidth += 1;
                        }
                    }
                }
                #endregion

                var sb = new StringBuilder();

                if (columnDefinitionLineIndex >= 0)
                {
                    //标题（可能不存在）
                    var headerStartIndex = 0;
                    if (lineArrays[0].Cells.Length == 1 && columnDefinitions.Count > 0)
                    {
                        headerStartIndex = 1;//表头行从索引值1开始算。

                        //如果第一行只有一个成员，算表的标题。
                        sb.Append("｜");
                        sb.Append(BuildTableCaptionText(lineArrays[0].Cells[0], columnDefinitions, insertColumnIndex));
                        sb.Append("｜\r\n｜");
                        sb.Append(BuildTableCaptionSplittorLine(columnDefinitions, insertColumnIndex));
                        sb.Append("｜\r\n");
                    }

                    //表头（可能不止一行），在
                    //removedColumnDefinitionLinesCount存在的意义是什么？用户可能编写超过一行的列定义行
                    for (int lineIndex = headerStartIndex; lineIndex < columnDefinitionLineIndex; lineIndex++)
                    {
                        //表格标题、表头均居中对齐
                        var cells = lineArrays[lineIndex];
                        sb.Append("｜");
                        for (int i = 0; i < maxColumn; i++)
                        {
                            int maxTextLength = columnDefinitions[i].MaxTextWidth;
                            if (i < cells.Cells.Length)
                            {
                                sb.Append(BuildCellText(cells.Cells[i], maxTextLength, ColumnAlignment.CENTER));
                                if (cells.Cells[i].Trim() == "序号" || cells.Cells[i].Trim().ToLower() == "no.")
                                {
                                    columnDefinitions[i].StartNumber = 0;
                                }
                            }
                            else
                            {
                                sb.Append(BuildCellText(null, maxTextLength, ColumnAlignment.CENTER));
                            }

                            if (insertColumnIndex != null && insertColumnIndex.HasValue && insertColumnIndex.Value == i)
                            {
                                sb.Append("｜   ｜");
                            }
                            else
                            {
                                sb.Append("｜");
                            }
                        }
                        sb.Append("\r\n");
                    }

                    //列定义行
                    if (columnDefinitionLineIndex >= 0)
                    {
                        sb.Append("｜");
                        for (int i = 0; i < maxColumn; i++)
                        {
                            int maxTextWidth = columnDefinitions[i].MaxTextWidth;
                            sb.Append(GetColumnDefinitionText(maxTextWidth, columnDefinitions[i].Align, columnDefinitions[i].StartNumber == 0));

                            if (insertColumnIndex != null && insertColumnIndex.HasValue && insertColumnIndex.Value == i)
                            {
                                sb.Append("｜---｜");
                            }
                            else
                            {
                                sb.Append("｜");
                            }
                        }
                        sb.Append("\r\n");
                    }

                    //表体（可能不存在）
                    for (int lineIndex = columnDefinitionLineIndex; lineIndex < lineArrays.Count; lineIndex++)
                    {
                        var cells = lineArrays[lineIndex];
                        sb.Append("｜");
                        for (int i = 0; i < maxColumn; i++)
                        {
                            int maxTextLength = columnDefinitions[i].MaxTextWidth;
                            if (i < cells.Cells.Length)
                            {
                                if (columnDefinitions[i].StartNumber.HasValue)
                                {
                                    columnDefinitions[i].StartNumber += 1;
                                    sb.Append(BuildCellText(columnDefinitions[i].StartNumber.Value.ToString("D2"), maxTextLength, columnDefinitions[i].Align));
                                }
                                else
                                {
                                    sb.Append(BuildCellText(cells.Cells[i], maxTextLength, columnDefinitions[i].Align));
                                }
                            }
                            else
                            {
                                sb.Append(BuildCellText(null, maxTextLength, columnDefinitions[i].Align));
                            }

                            if (insertColumnIndex != null && insertColumnIndex.HasValue && insertColumnIndex.Value == i)
                            {
                                sb.Append("｜   ｜");
                            }
                            else
                            {
                                sb.Append("｜");
                            }
                        }

                        if (lineArrays[lineIndex].Type == TableLineType.MergeLine)
                        {
                            sb.Append("^");
                        }
                        sb.Append("\r\n");
                    }

                    if (string.IsNullOrEmpty(commentText) == false) sb.Append("\r\n");
                }
                else
                {
                    //LMessageBox.Show("在附近文本中没有找到【表示表格中各列对齐方式】的那一行文本，无法格式化！\r\n" +
                    //"　　该文本一般会类似下面这行：\r\n\r\n" +
                    //"　　　　|:------|:------:|------:|\r\n\r\n" +
                    //"　　这行文本分别表示对应的列：【左对齐】、【居中对齐】、【右对齐】。",
                    //    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                    //return;

                    //如果没有“列对齐定义”这一行，就将所有列都视为表体列好了。

                    for (int lineIndex = 0; lineIndex < lineArrays.Count; lineIndex++)
                    {
                        var cells = lineArrays[lineIndex];
                        sb.Append("｜");
                        for (int i = 0; i < maxColumn; i++)
                        {
                            int maxTextLength = columnDefinitions[i].MaxTextWidth;
                            if (i < cells.Cells.Length)
                            {
                                sb.Append(BuildCellText(cells.Cells[i], maxTextLength, columnDefinitions[i].Align));
                            }
                            else
                            {
                                sb.Append(BuildCellText(null, maxTextLength, columnDefinitions[i].Align));
                            }

                            if (insertColumnIndex != null && insertColumnIndex.HasValue && insertColumnIndex.Value == i)
                            {
                                sb.Append("｜   ｜");
                            }
                            else
                            {
                                sb.Append("｜");
                            }
                        }

                        if (lineArrays[lineIndex].Type == TableLineType.MergeLine)
                        {
                            sb.Append("^");
                        }
                        sb.Append("\r\n");
                    }

                    if (string.IsNullOrEmpty(commentText) == false) sb.Append("\r\n");
                }

                var result = sb.ToString();
                if (srcTableText.EndsWith("\r\n") == false && result.EndsWith("\r\n"))
                {
                    result = result.Substring(0, result.Length - 2);
                }

                this.Document.Replace(startOffset, endOffset - startOffset, result + (commentText ?? ""));

                this.Document.UndoStack.EndUndoGroup();

                if (curLineNumber <= this.Document.LineCount)
                {
                    var cl = this.Document.GetLineByNumber(curLineNumber);
                    if (cl != null)
                    {
                        var selStart = cl.Offset + Math.Min(selStartOffset, cl.Length);
                        this.Select(selStart, 0);
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 生成文字表标题分割行文本。
        /// </summary>
        /// <param name="columnDefinitions">列定义集。</param>
        /// <param name="insertColumnIndex">列数。</param>
        private string BuildTableCaptionSplittorLine(List<TableColumnDefinition> columnDefinitions, int? insertColumnIndex)
        {
            if (columnDefinitions == null || columnDefinitions.Count <= 0) return string.Empty;

            var length = 0;
            foreach (var cd in columnDefinitions)
            {
                length += cd.MaxTextWidth;
                length += 2;
            }

            if (length > 2) length -= 2;

            if (insertColumnIndex.HasValue)
            {
                length += 5;////不需要考虑索引值到底是多少，只需要知道需要插入一列即可。一个空列是“   ｜”宽度为5。
            }

            var sb = new StringBuilder();
            for (int i = 0; i < length; i++)
            {
                sb.Append("=");
            }

            return sb.ToString();
        }

        /// <summary>
        /// 生成二维文字表的标题文本行。
        /// </summary>
        /// <param name="source">源文本。</param>
        /// <param name="columnDefinitions">列定义集合。</param>
        /// <param name="insertColumnIndex">列数。</param>
        private string BuildTableCaptionText(string source, List<TableColumnDefinition> columnDefinitions, int? insertColumnIndex)
        {
            if (columnDefinitions == null || columnDefinitions.Count <= 0) return source;

            var srcWidth = GetTextWidth(source);
            var sumTextWidth = 0;
            foreach (var cd in columnDefinitions)
            {
                sumTextWidth += cd.MaxTextWidth;
                sumTextWidth += 2;
            }

            if (sumTextWidth > 2) sumTextWidth -= 2;//末尾一个“｜”字符宽度不算。

            if (insertColumnIndex.HasValue)
            {
                sumTextWidth += 5;//不需要考虑索引值到底是多少，只需要知道需要插入一列即可。一个空列是“   ｜”宽度为5。
            }

            //表格标题居中对齐
            var sb = new StringBuilder();
            var offset = sumTextWidth - srcWidth;
            for (int i = 1; i <= offset / 2; i++)
            {
                sb.Append(" ");
            }

            string result;
            if ((int)(offset / 2) * 2 + 1 == offset)
            {
                result = sb.ToString() + (source ?? "") + sb.ToString() + " ";
            }
            else
            {
                result = sb.ToString() + (source ?? "") + sb.ToString();
            }

            return result;
        }

        /// <summary>
        /// 取出列定义文本。
        /// </summary>
        /// <param name="maxLength">最大长度。</param>
        /// <param name="align">列对齐方式。</param>
        /// <param name="withSortMark">是否带“填充序号”标志符（^）。</param>
        /// <returns></returns>
        private string GetColumnDefinitionText(int maxLength, ColumnAlignment align, bool withSortMark)
        {
            if (withSortMark)
            {
                switch (align)
                {
                    case ColumnAlignment.LEFT:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < (maxLength - 2); i++)
                            {
                                sb.Append("-");
                            }
                            return ":" + sb.ToString() + "^";
                        }
                    case ColumnAlignment.RIGHT:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < (maxLength - 2); i++)
                            {
                                sb.Append("-");
                            }
                            return sb.ToString() + "^:";
                        }
                    case ColumnAlignment.CENTER:
                    default:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < maxLength - 3; i++)
                            {
                                sb.Append("-");
                            }
                            return ":" + sb.ToString() + "^:";
                        }
                }
            }
            else
            {
                switch (align)
                {
                    case ColumnAlignment.LEFT:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < (maxLength - 1); i++)
                            {
                                sb.Append("-");
                            }
                            return ":" + sb.ToString();
                        }
                    case ColumnAlignment.RIGHT:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < (maxLength - 1); i++)
                            {
                                sb.Append("-");
                            }
                            return sb.ToString() + ":";
                        }
                    case ColumnAlignment.CENTER:
                    default:
                        {
                            var sb = new StringBuilder();
                            for (int i = 0; i < maxLength - 2; i++)
                            {
                                sb.Append("-");
                            }
                            return ":" + sb.ToString() + ":";
                        }
                }
            }
        }

        /// <summary>
        /// 取文本宽度。
        /// </summary>
        /// <param name="text">要取宽度的文本</param>
        /// <param name="trimStartAndEnd">是否去除首尾空格。</param>
        /// <returns></returns>
        public static int GetTextWidth(string text, bool trimStartAndEnd = false)
        {
            if (string.IsNullOrEmpty(text)) return 0;
            if (CustomMarkdownSupport.IsColumnAlignmentDefinitionLine(text)) return 0;

            if (trimStartAndEnd) text = text.Trim();

            int width = 0;
            foreach (char c in text)
            {
                width += Encoding.Default.GetByteCount(new char[] { c });//判断全、半角不需要再手工做了。
            }

            return width;
        }

        /// <summary>
        /// 用半角空格填充，使单元格中文本能够对齐。
        /// </summary>
        /// <param name="source">要填空空格的源文本。</param>
        /// <param name="maxTextWidth">最大宽度。</param>
        /// <param name="align">对齐方式</param>
        private string BuildCellText(string source, int maxTextWidth, ColumnAlignment align)
        {
            var srcWidth = GetTextWidth(source);

            string result;
            switch (align)
            {
                case ColumnAlignment.LEFT:
                    {
                        var sb = new StringBuilder();
                        for (int i = 1; i <= maxTextWidth - srcWidth; i++)
                        {
                            sb.Append(" ");
                        }
                        result = (source ?? "") + sb.ToString();
                        break;
                    }
                case ColumnAlignment.RIGHT:
                    {
                        var sb = new StringBuilder();
                        for (int i = 1; i <= maxTextWidth - srcWidth; i++)
                        {
                            sb.Append(" ");
                        }
                        result = sb.ToString() + (source ?? "");
                        break;
                    }
                //case ColumnDefinition.CENTER:
                default:
                    {
                        var sb = new StringBuilder();
                        var offset = maxTextWidth - srcWidth;
                        for (int i = 1; i <= offset / 2; i++)
                        {
                            sb.Append(" ");
                        }

                        if ((int)(offset / 2) * 2 + 1 == offset)
                        {
                            result = sb.ToString() + (source ?? "") + sb.ToString() + " ";
                            break;
                        }
                        else
                        {
                            result = sb.ToString() + (source ?? "") + sb.ToString();
                            break;
                        }
                    }
            }
            return result;
        }

        /// <summary>
        /// 根据指定文本偏移位置，取附近属于二维文字表的所有文本行。
        /// </summary>
        /// <param name="startOffset">文本偏移位置（头索引）。</param>
        /// <param name="endOffset">文本偏移位置（尾索引）</param>
        /// <param name="commentText">注释文本。</param>
        /// <returns></returns>
        private string FindSourceTableText(out int startOffset, out int endOffset, out string commentText)
        {
            var startSelLine = this.Document.GetLineByOffset(this.SelectionStart);
            var endSelLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);

            if (startSelLine == null || endSelLine == null)
            {
                startOffset = endOffset = -1;
                commentText = null;
                return null;
            }

            if (startSelLine.LineNumber == endSelLine.LineNumber)
            {
                var text = this.Document.GetText(startSelLine.Offset, startSelLine.Length);
                if (string.IsNullOrEmpty(text) || (text.Contains("|") == false && text.Contains("｜") == false))
                {
                    startOffset = endOffset = -1;
                    commentText = null;
                    return null;
                }
            }

            //基本思路：向上、向下找到表格的首、尾两行。
            var commentString = new StringBuilder();

            int i = startSelLine.LineNumber;//LineNumber要比索引大1。索引仍然是从0开始计算的。
            while (i >= 1)
            {
                var line = this.Document.GetLineByNumber(i);
                if (line == null) break;

                var previewLineText = this.Document.GetText(line.Offset, line.Length);

                if (previewLineText.StartsWith(";") || previewLineText.StartsWith("；"))
                {
                    //注释不算
                    commentString.Insert(0, previewLineText + "\r\n");
                    i--;
                    continue;
                }

                if (previewLineText.Contains("|") == false && previewLineText.Contains("｜") == false) break;

                i--;
            }

            int fstLineNumber = i + 1;

            i = endSelLine.LineNumber;
            while (i <= this.Document.LineCount)
            {
                var line = this.Document.GetLineByNumber(i);
                if (line == null) break;

                var nextLineText = this.Document.GetText(line.Offset, line.Length);

                if (nextLineText.StartsWith(";") || nextLineText.StartsWith("；"))
                {
                    //注释不算
                    commentString.Append(nextLineText + "\r\n");
                    i++;
                    continue;
                }

                if (nextLineText.Contains("|") == false && nextLineText.Contains("｜") == false) break;

                i++;
            }

            int lastLineNumber = i - 1;

            startOffset = this.Document.GetLineByNumber(fstLineNumber).Offset;
            endOffset = this.Document.GetLineByNumber(lastLineNumber).EndOffset;
            commentText = commentString.ToString();

            var srcText = this.Document.GetText(startOffset, endOffset - startOffset);

            return srcText;
        }

        /// <summary>
        /// 判断此行文本是否只包含“｜”、“=”等。这样的行是“表格格式化功能”自动添加的分割表标题与表头行的装饰行。
        /// </summary>
        /// <param name="lineText">文本行</param>
        private bool IsTableCaptionSplittor(string lineText)
        {
            if (string.IsNullOrEmpty(lineText)) return false;

            foreach (char c in lineText)
            {
                if (c == '=' || c == '｜' || c == '|' || c == '＝' ||
                    c == ' ' ||                      //下面这三个是考虑到在<code>块中的文字表也需要格式化。
                    c == '　' ||
                    c == '\t')
                    continue;

                return false;
            }

            return true;
        }

        /// <summary>
        /// 给二维文字表插入列。
        /// </summary>
        internal void InsertTableColumn()
        {
            var selEndOffset = this.SelectionStart + this.SelectionLength;
            var curLine = this.Document.GetLineByOffset(selEndOffset);
            var curLineLeftText = this.Document.GetText(curLine.Offset, selEndOffset - curLine.Offset);

            int pipesCount = 0;
            foreach (char c in curLineLeftText)
            {
                if (c == '|' || c == '｜')
                {
                    pipesCount++;
                }
            }

            int? insertColumnIndex = null;
            if (curLineLeftText.StartsWith("|") || curLineLeftText.StartsWith("｜"))
            {
                insertColumnIndex = pipesCount;
            }
            else
            {
                insertColumnIndex = pipesCount + 1;
            }

            Format2DTextTable(insertColumnIndex.Value - 1);

            JumpToNextCell();
            if (Globals.MainWindow.miSelectCellFirst.IsChecked)
            {
                JumpToNextCell();//再调用一次，选中新插入的列在当前行的单元格。
            }
        }
    }

    public class TableCellArray
    {
        public string[] Cells { get; set; }

        public TableLineType Type { get; set; }
    }
}
